mirror of
https://github.com/DarkflameUniverse/DarkflameServer.git
synced 2024-11-23 14:07:20 +00:00
Merge remote-tracking branch 'origin/main' into scripting-lua
This commit is contained in:
commit
ac7823802e
@ -19,6 +19,12 @@ void AndBehavior::Calculate(BehaviorContext* context, RakNet::BitStream* bitStre
|
||||
}
|
||||
}
|
||||
|
||||
void AndBehavior::UnCast(BehaviorContext* context, const BehaviorBranchContext branch) {
|
||||
for (auto behavior : this->m_behaviors) {
|
||||
behavior->UnCast(context, branch);
|
||||
}
|
||||
}
|
||||
|
||||
void AndBehavior::Load()
|
||||
{
|
||||
const auto parameters = GetParameterNames();
|
||||
|
@ -20,5 +20,7 @@ public:
|
||||
|
||||
void Calculate(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) override;
|
||||
|
||||
void UnCast(BehaviorContext* context, BehaviorBranchContext branch) override;
|
||||
|
||||
void Load() override;
|
||||
};
|
||||
|
@ -14,7 +14,7 @@ void BasicAttackBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bi
|
||||
auto* destroyableComponent = entity->GetComponent<DestroyableComponent>();
|
||||
if (destroyableComponent != nullptr) {
|
||||
PlayFx(u"onhit", entity->GetObjectID());
|
||||
destroyableComponent->Damage(this->m_maxDamage, context->originator);
|
||||
destroyableComponent->Damage(this->m_maxDamage, context->originator, context->skillID);
|
||||
}
|
||||
|
||||
this->m_onSuccess->Handle(context, bitStream, branch);
|
||||
@ -56,7 +56,7 @@ void BasicAttackBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bi
|
||||
auto* destroyableComponent = entity->GetComponent<DestroyableComponent>();
|
||||
if (destroyableComponent != nullptr) {
|
||||
PlayFx(u"onhit", entity->GetObjectID());
|
||||
destroyableComponent->Damage(damageDealt, context->originator);
|
||||
destroyableComponent->Damage(damageDealt, context->originator, context->skillID);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -113,7 +113,7 @@ void BasicAttackBehavior::Calculate(BehaviorContext* context, RakNet::BitStream*
|
||||
auto* destroyableComponent = entity->GetComponent<DestroyableComponent>();
|
||||
if (damage != 0 && destroyableComponent != nullptr) {
|
||||
PlayFx(u"onhit", entity->GetObjectID(), 1);
|
||||
destroyableComponent->Damage(damage, context->originator, false);
|
||||
destroyableComponent->Damage(damage, context->originator, context->skillID, false);
|
||||
context->ScheduleUpdate(branch.target);
|
||||
}
|
||||
}
|
||||
|
@ -18,6 +18,7 @@
|
||||
#include "AreaOfEffectBehavior.h"
|
||||
#include "DurationBehavior.h"
|
||||
#include "TacArcBehavior.h"
|
||||
#include "LootBuffBehavior.h"
|
||||
#include "AttackDelayBehavior.h"
|
||||
#include "BasicAttackBehavior.h"
|
||||
#include "ChainBehavior.h"
|
||||
@ -172,7 +173,9 @@ Behavior* Behavior::CreateBehavior(const uint32_t behaviorId)
|
||||
behavior = new SpeedBehavior(behaviorId);
|
||||
break;
|
||||
case BehaviorTemplates::BEHAVIOR_DARK_INSPIRATION: break;
|
||||
case BehaviorTemplates::BEHAVIOR_LOOT_BUFF: break;
|
||||
case BehaviorTemplates::BEHAVIOR_LOOT_BUFF:
|
||||
behavior = new LootBuffBehavior(behaviorId);
|
||||
break;
|
||||
case BehaviorTemplates::BEHAVIOR_VENTURE_VISION: break;
|
||||
case BehaviorTemplates::BEHAVIOR_SPAWN_OBJECT:
|
||||
behavior = new SpawnBehavior(behaviorId);
|
||||
|
@ -64,8 +64,8 @@ void BehaviorContext::RegisterSyncBehavior(const uint32_t syncId, Behavior* beha
|
||||
|
||||
void BehaviorContext::RegisterTimerBehavior(Behavior* behavior, const BehaviorBranchContext& branchContext, const LWOOBJID second)
|
||||
{
|
||||
BehaviorTimerEntry entry
|
||||
;
|
||||
BehaviorTimerEntry entry;
|
||||
|
||||
entry.time = branchContext.duration;
|
||||
entry.behavior = behavior;
|
||||
entry.branchContext = branchContext;
|
||||
|
@ -58,6 +58,8 @@ struct BehaviorContext
|
||||
|
||||
float skillTime = 0;
|
||||
|
||||
uint32_t skillID = 0;
|
||||
|
||||
uint32_t skillUId = 0;
|
||||
|
||||
bool failed = false;
|
||||
|
38
dGame/dBehaviors/LootBuffBehavior.cpp
Normal file
38
dGame/dBehaviors/LootBuffBehavior.cpp
Normal file
@ -0,0 +1,38 @@
|
||||
#include "LootBuffBehavior.h"
|
||||
|
||||
void LootBuffBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) {
|
||||
auto target = EntityManager::Instance()->GetEntity(context->caster);
|
||||
if (!target) return;
|
||||
|
||||
auto controllablePhysicsComponent = target->GetComponent<ControllablePhysicsComponent>();
|
||||
if (!controllablePhysicsComponent) return;
|
||||
|
||||
controllablePhysicsComponent->AddPickupRadiusScale(m_Scale);
|
||||
EntityManager::Instance()->SerializeEntity(target);
|
||||
|
||||
if (branch.duration > 0) context->RegisterTimerBehavior(this, branch);
|
||||
|
||||
}
|
||||
|
||||
void LootBuffBehavior::Calculate(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) {
|
||||
Handle(context, bitStream, branch);
|
||||
}
|
||||
|
||||
void LootBuffBehavior::UnCast(BehaviorContext* context, BehaviorBranchContext branch) {
|
||||
auto target = EntityManager::Instance()->GetEntity(context->caster);
|
||||
if (!target) return;
|
||||
|
||||
auto controllablePhysicsComponent = target->GetComponent<ControllablePhysicsComponent>();
|
||||
if (!controllablePhysicsComponent) return;
|
||||
|
||||
controllablePhysicsComponent->RemovePickupRadiusScale(m_Scale);
|
||||
EntityManager::Instance()->SerializeEntity(target);
|
||||
}
|
||||
|
||||
void LootBuffBehavior::Timer(BehaviorContext* context, BehaviorBranchContext branch, LWOOBJID second) {
|
||||
UnCast(context, branch);
|
||||
}
|
||||
|
||||
void LootBuffBehavior::Load() {
|
||||
this->m_Scale = GetFloat("scale");
|
||||
}
|
32
dGame/dBehaviors/LootBuffBehavior.h
Normal file
32
dGame/dBehaviors/LootBuffBehavior.h
Normal file
@ -0,0 +1,32 @@
|
||||
#pragma once
|
||||
#include "Behavior.h"
|
||||
#include "BehaviorBranchContext.h"
|
||||
#include "BehaviorContext.h"
|
||||
#include "ControllablePhysicsComponent.h"
|
||||
|
||||
/**
|
||||
* @brief This is the behavior class to be used for all Loot Buff behavior nodes in the Behavior tree.
|
||||
*
|
||||
*/
|
||||
class LootBuffBehavior final : public Behavior
|
||||
{
|
||||
public:
|
||||
|
||||
float m_Scale;
|
||||
|
||||
/*
|
||||
* Inherited
|
||||
*/
|
||||
|
||||
explicit LootBuffBehavior(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 UnCast(BehaviorContext* context, BehaviorBranchContext branch) override;
|
||||
|
||||
void Timer(BehaviorContext* context, BehaviorBranchContext branch, LWOOBJID second) override;
|
||||
|
||||
void Load() override;
|
||||
};
|
@ -7,62 +7,26 @@
|
||||
#include "SkillComponent.h"
|
||||
#include "DestroyableComponent.h"
|
||||
|
||||
/**
|
||||
* The OverTime behavior is very inconsistent in how it appears in the skill tree vs. how it should behave.
|
||||
*
|
||||
* Items like "Doc in a Box" use an overtime behavior which you would expect have health & armor regen, but is only fallowed by a stun.
|
||||
*
|
||||
* Due to this inconsistency, we have to implement a special case for some items.
|
||||
*/
|
||||
|
||||
void OverTimeBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch)
|
||||
{
|
||||
const auto originator = context->originator;
|
||||
|
||||
auto* entity = EntityManager::Instance()->GetEntity(originator);
|
||||
|
||||
if (entity == nullptr)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (entity == nullptr) return;
|
||||
|
||||
for (size_t i = 0; i < m_NumIntervals; i++)
|
||||
{
|
||||
entity->AddCallbackTimer((i + 1) * m_Delay, [originator, branch, this]() {
|
||||
auto* entity = EntityManager::Instance()->GetEntity(originator);
|
||||
|
||||
if (entity == nullptr)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (entity == nullptr) return;
|
||||
|
||||
auto* skillComponent = entity->GetComponent<SkillComponent>();
|
||||
|
||||
if (skillComponent == nullptr)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (skillComponent == nullptr) return;
|
||||
|
||||
skillComponent->CalculateBehavior(0, m_Action->m_behaviorId, branch.target, true, true);
|
||||
|
||||
auto* destroyableComponent = entity->GetComponent<DestroyableComponent>();
|
||||
|
||||
if (destroyableComponent == nullptr)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Special cases for inconsistent behavior.
|
||||
*/
|
||||
|
||||
switch (m_behaviorId)
|
||||
{
|
||||
case 26253: // "Doc in a Box", heal up to 6 health and regen up to 18 armor.
|
||||
destroyableComponent->Heal(1);
|
||||
destroyableComponent->Repair(3);
|
||||
break;
|
||||
}
|
||||
skillComponent->CalculateBehavior(m_Action, m_ActionBehaviorId, branch.target, true, true);
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -74,7 +38,12 @@ void OverTimeBehavior::Calculate(BehaviorContext* context, RakNet::BitStream* bi
|
||||
|
||||
void OverTimeBehavior::Load()
|
||||
{
|
||||
m_Action = GetAction("action");
|
||||
m_Action = GetInt("action");
|
||||
// Since m_Action is a skillID and not a behavior, get is correlated behaviorID.
|
||||
|
||||
CDSkillBehaviorTable* skillTable = CDClientManager::Instance()->GetTable<CDSkillBehaviorTable>("SkillBehavior");
|
||||
m_ActionBehaviorId = skillTable->GetSkillByID(m_Action).behaviorID;
|
||||
|
||||
m_Delay = GetFloat("delay");
|
||||
m_NumIntervals = GetInt("num_intervals");
|
||||
}
|
||||
|
@ -4,7 +4,8 @@
|
||||
class OverTimeBehavior final : public Behavior
|
||||
{
|
||||
public:
|
||||
Behavior* m_Action;
|
||||
uint32_t m_Action;
|
||||
uint32_t m_ActionBehaviorId;
|
||||
float m_Delay;
|
||||
int32_t m_NumIntervals;
|
||||
|
||||
|
@ -29,6 +29,8 @@ ControllablePhysicsComponent::ControllablePhysicsComponent(Entity* entity) : Com
|
||||
m_GravityScale = 1;
|
||||
m_DirtyCheats = false;
|
||||
m_IgnoreMultipliers = false;
|
||||
m_PickupRadius = 0.0f;
|
||||
m_DirtyPickupRadiusScale = true;
|
||||
|
||||
if (entity->GetLOT() != 1) // Other physics entities we care about will be added by BaseCombatAI
|
||||
return;
|
||||
@ -85,7 +87,13 @@ void ControllablePhysicsComponent::Serialize(RakNet::BitStream* outBitStream, bo
|
||||
m_DirtyCheats = false;
|
||||
}
|
||||
|
||||
outBitStream->Write0();
|
||||
outBitStream->Write(m_DirtyPickupRadiusScale);
|
||||
if (m_DirtyPickupRadiusScale) {
|
||||
outBitStream->Write(m_PickupRadius);
|
||||
outBitStream->Write0(); //No clue what this is so im leaving it false.
|
||||
m_DirtyPickupRadiusScale = false;
|
||||
}
|
||||
|
||||
outBitStream->Write0();
|
||||
|
||||
outBitStream->Write(m_DirtyPosition || bIsInitialUpdate);
|
||||
@ -230,4 +238,32 @@ void ControllablePhysicsComponent::SetDirtyVelocity(bool val) {
|
||||
|
||||
void ControllablePhysicsComponent::SetDirtyAngularVelocity(bool val) {
|
||||
m_DirtyAngularVelocity = val;
|
||||
}
|
||||
}
|
||||
|
||||
void ControllablePhysicsComponent::AddPickupRadiusScale(float value) {
|
||||
m_ActivePickupRadiusScales.push_back(value);
|
||||
if (value > m_PickupRadius) {
|
||||
m_PickupRadius = value;
|
||||
m_DirtyPickupRadiusScale = true;
|
||||
}
|
||||
}
|
||||
|
||||
void ControllablePhysicsComponent::RemovePickupRadiusScale(float value) {
|
||||
// Attempt to remove pickup radius from active radii
|
||||
const auto pos = std::find(m_ActivePickupRadiusScales.begin(), m_ActivePickupRadiusScales.end(), value);
|
||||
if (pos != m_ActivePickupRadiusScales.end()) {
|
||||
m_ActivePickupRadiusScales.erase(pos);
|
||||
} else {
|
||||
Game::logger->Log("ControllablePhysicsComponent", "Warning: Could not find pickup radius %f in list of active radii. List has %i active radii.\n", value, m_ActivePickupRadiusScales.size());
|
||||
return;
|
||||
}
|
||||
|
||||
// Recalculate pickup radius since we removed one by now
|
||||
m_PickupRadius = 0.0f;
|
||||
m_DirtyPickupRadiusScale = true;
|
||||
for (uint32_t i = 0; i < m_ActivePickupRadiusScales.size(); i++) {
|
||||
auto candidateRadius = m_ActivePickupRadiusScales[i];
|
||||
if (m_PickupRadius < candidateRadius) m_PickupRadius = candidateRadius;
|
||||
}
|
||||
EntityManager::Instance()->SerializeEntity(m_Parent);
|
||||
}
|
||||
|
@ -227,6 +227,24 @@ public:
|
||||
|
||||
dpEntity* GetdpEntity() const { return m_dpEntity; }
|
||||
|
||||
/**
|
||||
* I store this in a vector because if I have 2 separate pickup radii being applied to the player, I dont know which one is correctly active.
|
||||
* This method adds the pickup radius to the vector of active radii and if its larger than the current one, is applied as the new pickup radius.
|
||||
*/
|
||||
void AddPickupRadiusScale(float value) ;
|
||||
|
||||
/**
|
||||
* Removes the provided pickup radius scale from our list of buffs
|
||||
* The recalculates what our pickup radius is.
|
||||
*/
|
||||
void RemovePickupRadiusScale(float value) ;
|
||||
|
||||
/**
|
||||
* The pickup radii of this component.
|
||||
* @return All active radii scales for this component.
|
||||
*/
|
||||
std::vector<float> GetActivePickupRadiusScales() { return m_ActivePickupRadiusScales; };
|
||||
|
||||
private:
|
||||
/**
|
||||
* The entity that owns this component
|
||||
@ -322,6 +340,21 @@ private:
|
||||
* Whether this entity is static, making it unable to move
|
||||
*/
|
||||
bool m_Static;
|
||||
|
||||
/**
|
||||
* Whether the pickup scale is dirty.
|
||||
*/
|
||||
bool m_DirtyPickupRadiusScale;
|
||||
|
||||
/**
|
||||
* The list of pickup radius scales for this entity
|
||||
*/
|
||||
std::vector<float> m_ActivePickupRadiusScales;
|
||||
|
||||
/**
|
||||
* The active pickup radius for this entity
|
||||
*/
|
||||
float m_PickupRadius;
|
||||
};
|
||||
|
||||
#endif // CONTROLLABLEPHYSICSCOMPONENT_H
|
||||
|
@ -593,7 +593,7 @@ void DestroyableComponent::Repair(const uint32_t armor)
|
||||
}
|
||||
|
||||
|
||||
void DestroyableComponent::Damage(uint32_t damage, const LWOOBJID source, bool echo)
|
||||
void DestroyableComponent::Damage(uint32_t damage, const LWOOBJID source, uint32_t skillID, bool echo)
|
||||
{
|
||||
if (GetHealth() <= 0)
|
||||
{
|
||||
@ -677,11 +677,10 @@ void DestroyableComponent::Damage(uint32_t damage, const LWOOBJID source, bool e
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
Smash(source);
|
||||
Smash(source, eKillType::VIOLENT, u"", skillID);
|
||||
}
|
||||
|
||||
void DestroyableComponent::Smash(const LWOOBJID source, const eKillType killType, const std::u16string& deathType)
|
||||
void DestroyableComponent::Smash(const LWOOBJID source, const eKillType killType, const std::u16string& deathType, uint32_t skillID)
|
||||
{
|
||||
if (m_iHealth > 0)
|
||||
{
|
||||
@ -727,31 +726,20 @@ void DestroyableComponent::Smash(const LWOOBJID source, const eKillType killType
|
||||
if (memberMissions == nullptr) continue;
|
||||
|
||||
memberMissions->Progress(MissionTaskType::MISSION_TASK_TYPE_SMASH, m_Parent->GetLOT());
|
||||
memberMissions->Progress(MissionTaskType::MISSION_TASK_TYPE_SKILL, m_Parent->GetLOT(), skillID);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
missions->Progress(MissionTaskType::MISSION_TASK_TYPE_SMASH, m_Parent->GetLOT());
|
||||
missions->Progress(MissionTaskType::MISSION_TASK_TYPE_SKILL, m_Parent->GetLOT(), skillID);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const auto isPlayer = m_Parent->IsPlayer();
|
||||
|
||||
GameMessages::SendDie(
|
||||
m_Parent,
|
||||
source,
|
||||
source,
|
||||
true,
|
||||
killType,
|
||||
deathType,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
isPlayer,
|
||||
false,
|
||||
1
|
||||
);
|
||||
GameMessages::SendDie(m_Parent, source, source, true, killType, deathType, 0, 0, 0, isPlayer, false, 1);
|
||||
|
||||
//NANI?!
|
||||
if (!isPlayer)
|
||||
|
@ -377,17 +377,19 @@ public:
|
||||
* Attempt to damage this entity, handles everything from health and armor to absorption, immunity and callbacks.
|
||||
* @param damage the damage to attempt to apply
|
||||
* @param source the attacker that caused this damage
|
||||
* @param skillID the skill that damaged this entity
|
||||
* @param echo whether or not to serialize the damage
|
||||
*/
|
||||
void Damage(uint32_t damage, LWOOBJID source, bool echo = true);
|
||||
void Damage(uint32_t damage, LWOOBJID source, uint32_t skillID = 0, bool echo = true);
|
||||
|
||||
/**
|
||||
* Smashes this entity, notifying all clients
|
||||
* @param source the source that smashed this entity
|
||||
* @param skillID the skill that killed this entity
|
||||
* @param killType the way this entity was killed, determines if a client animation is played
|
||||
* @param deathType the animation to play when killed
|
||||
*/
|
||||
void Smash(LWOOBJID source, eKillType killType = eKillType::VIOLENT, const std::u16string& deathType = u"");
|
||||
void Smash(LWOOBJID source, eKillType killType = eKillType::VIOLENT, const std::u16string& deathType = u"", uint32_t skillID = 0);
|
||||
|
||||
/**
|
||||
* Pushes a layer of immunity to this entity, making it immune for longer
|
||||
|
@ -25,12 +25,14 @@ ProjectileSyncEntry::ProjectileSyncEntry()
|
||||
{
|
||||
}
|
||||
|
||||
bool SkillComponent::CastPlayerSkill(const uint32_t behaviorId, const uint32_t skillUid, RakNet::BitStream* bitStream, const LWOOBJID target)
|
||||
bool SkillComponent::CastPlayerSkill(const uint32_t behaviorId, const uint32_t skillUid, RakNet::BitStream* bitStream, const LWOOBJID target, uint32_t skillID)
|
||||
{
|
||||
auto* context = new BehaviorContext(this->m_Parent->GetObjectID());
|
||||
|
||||
context->caster = m_Parent->GetObjectID();
|
||||
|
||||
context->skillID = skillID;
|
||||
|
||||
this->m_managedBehaviors.insert_or_assign(skillUid, context);
|
||||
|
||||
auto* behavior = Behavior::CreateBehavior(behaviorId);
|
||||
|
@ -92,7 +92,7 @@ public:
|
||||
* @param bitStream the bitSteam given by the client to determine the behavior path
|
||||
* @param target the explicit target of the skill
|
||||
*/
|
||||
bool CastPlayerSkill(uint32_t behaviorId, uint32_t skillUid, RakNet::BitStream* bitStream, LWOOBJID target);
|
||||
bool CastPlayerSkill(uint32_t behaviorId, uint32_t skillUid, RakNet::BitStream* bitStream, LWOOBJID target, uint32_t skillID = 0);
|
||||
|
||||
/**
|
||||
* Continues a player skill. Should only be called when the server receives a sync message from the client.
|
||||
|
@ -1,116 +1,131 @@
|
||||
#include "VendorComponent.h"
|
||||
#include "Game.h"
|
||||
#include "dServer.h"
|
||||
|
||||
#include <BitStream.h>
|
||||
|
||||
#include "Game.h"
|
||||
#include "dServer.h"
|
||||
|
||||
VendorComponent::VendorComponent(Entity* parent) : Component(parent) {
|
||||
auto* compRegistryTable = CDClientManager::Instance()->GetTable<CDComponentsRegistryTable>("ComponentsRegistry");
|
||||
auto* vendorComponentTable = CDClientManager::Instance()->GetTable<CDVendorComponentTable>("VendorComponent");
|
||||
auto* lootMatrixTable = CDClientManager::Instance()->GetTable<CDLootMatrixTable>("LootMatrix");
|
||||
auto* lootTableTable = CDClientManager::Instance()->GetTable<CDLootTableTable>("LootTable");
|
||||
|
||||
int componentID = compRegistryTable->GetByIDAndType(m_Parent->GetLOT(), COMPONENT_TYPE_VENDOR);
|
||||
std::vector<CDVendorComponent> vendorComps = vendorComponentTable->Query([=](CDVendorComponent entry) { return (entry.id == componentID); });
|
||||
if (vendorComps.empty()) {
|
||||
return;
|
||||
}
|
||||
m_BuyScalar = vendorComps[0].buyScalar;
|
||||
m_SellScalar = vendorComps[0].sellScalar;
|
||||
int lootMatrixID = vendorComps[0].LootMatrixIndex;
|
||||
std::vector<CDLootMatrix> lootMatrices = lootMatrixTable->Query([=](CDLootMatrix entry) { return (entry.LootMatrixIndex == lootMatrixID); });
|
||||
if (lootMatrices.empty()) {
|
||||
return;
|
||||
}
|
||||
for (const auto& lootMatrix : lootMatrices) {
|
||||
int lootTableID = lootMatrix.LootTableIndex;
|
||||
std::vector<CDLootTable> vendorItems = lootTableTable->Query([=](CDLootTable entry) { return (entry.LootTableIndex == lootTableID); });
|
||||
if (lootMatrix.maxToDrop == 0 || lootMatrix.minToDrop == 0) {
|
||||
for (CDLootTable item : vendorItems) {
|
||||
m_Inventory.insert({item.itemid, item.sortPriority});
|
||||
}
|
||||
} else {
|
||||
auto randomCount = GeneralUtils::GenerateRandomNumber<int32_t>(lootMatrix.minToDrop, lootMatrix.maxToDrop);
|
||||
|
||||
for (size_t i = 0; i < randomCount; i++) {
|
||||
if (vendorItems.empty()) {
|
||||
break;
|
||||
}
|
||||
|
||||
auto randomItemIndex = GeneralUtils::GenerateRandomNumber<int32_t>(0, vendorItems.size() - 1);
|
||||
|
||||
const auto& randomItem = vendorItems[randomItemIndex];
|
||||
|
||||
vendorItems.erase(vendorItems.begin() + randomItemIndex);
|
||||
|
||||
m_Inventory.insert({randomItem.itemid, randomItem.sortPriority});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Because I want a vendor to sell these cameras
|
||||
if (parent->GetLOT() == 13569) {
|
||||
auto randomCamera = GeneralUtils::GenerateRandomNumber<int32_t>(0, 2);
|
||||
|
||||
switch (randomCamera) {
|
||||
case 0:
|
||||
m_Inventory.insert({16253, 0}); //Grungagroid
|
||||
break;
|
||||
case 1:
|
||||
m_Inventory.insert({16254, 0}); //Hipstabrick
|
||||
break;
|
||||
case 2:
|
||||
m_Inventory.insert({16204, 0}); //Megabrixel snapshot
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//Custom code for Max vanity NPC
|
||||
if (parent->GetLOT() == 9749 && Game::server->GetZoneID() == 1201) {
|
||||
m_Inventory.clear();
|
||||
m_Inventory.insert({11909, 0}); //Top hat w frog
|
||||
m_Inventory.insert({7785, 0}); //Flash bulb
|
||||
m_Inventory.insert({12764, 0}); //Big fountain soda
|
||||
m_Inventory.insert({12241, 0}); //Hot cocoa (from fb)
|
||||
}
|
||||
SetupConstants();
|
||||
RefreshInventory(true);
|
||||
}
|
||||
|
||||
VendorComponent::~VendorComponent() = default;
|
||||
|
||||
void VendorComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags) {
|
||||
outBitStream->Write1();
|
||||
outBitStream->Write1(); // this bit is REQUIRED for vendor + mission multiinteract
|
||||
outBitStream->Write(HasCraftingStation());
|
||||
outBitStream->Write1();
|
||||
outBitStream->Write1(); // Has standard items (Required for vendors with missions.)
|
||||
outBitStream->Write(HasCraftingStation()); // Has multi use items
|
||||
}
|
||||
|
||||
void VendorComponent::OnUse(Entity* originator) {
|
||||
GameMessages::SendVendorOpenWindow(m_Parent, originator->GetSystemAddress());
|
||||
GameMessages::SendVendorStatusUpdate(m_Parent, originator->GetSystemAddress());
|
||||
GameMessages::SendVendorOpenWindow(m_Parent, originator->GetSystemAddress());
|
||||
GameMessages::SendVendorStatusUpdate(m_Parent, originator->GetSystemAddress());
|
||||
}
|
||||
|
||||
float VendorComponent::GetBuyScalar() const {
|
||||
return m_BuyScalar;
|
||||
return m_BuyScalar;
|
||||
}
|
||||
|
||||
float VendorComponent::GetSellScalar() const {
|
||||
return m_SellScalar;
|
||||
return m_SellScalar;
|
||||
}
|
||||
|
||||
void VendorComponent::SetBuyScalar(float value) {
|
||||
m_BuyScalar = value;
|
||||
m_BuyScalar = value;
|
||||
}
|
||||
|
||||
void VendorComponent::SetSellScalar(float value) {
|
||||
m_SellScalar = value;
|
||||
m_SellScalar = value;
|
||||
}
|
||||
|
||||
std::map<LOT, int>& VendorComponent::GetInventory() {
|
||||
return m_Inventory;
|
||||
return m_Inventory;
|
||||
}
|
||||
|
||||
bool VendorComponent::HasCraftingStation() {
|
||||
// As far as we know, only Umami has a crafting station
|
||||
return m_Parent->GetLOT() == 13800;
|
||||
// As far as we know, only Umami has a crafting station
|
||||
return m_Parent->GetLOT() == 13800;
|
||||
}
|
||||
|
||||
void VendorComponent::RefreshInventory(bool isCreation) {
|
||||
//Custom code for Max vanity NPC
|
||||
if (m_Parent->GetLOT() == 9749 && Game::server->GetZoneID() == 1201) {
|
||||
if (!isCreation) return;
|
||||
m_Inventory.insert({11909, 0}); //Top hat w frog
|
||||
m_Inventory.insert({7785, 0}); //Flash bulb
|
||||
m_Inventory.insert({12764, 0}); //Big fountain soda
|
||||
m_Inventory.insert({12241, 0}); //Hot cocoa (from fb)
|
||||
return;
|
||||
}
|
||||
m_Inventory.clear();
|
||||
auto* lootMatrixTable = CDClientManager::Instance()->GetTable<CDLootMatrixTable>("LootMatrix");
|
||||
std::vector<CDLootMatrix> lootMatrices = lootMatrixTable->Query([=](CDLootMatrix entry) { return (entry.LootMatrixIndex == m_LootMatrixID); });
|
||||
|
||||
if (lootMatrices.empty()) return;
|
||||
// Done with lootMatrix table
|
||||
|
||||
auto* lootTableTable = CDClientManager::Instance()->GetTable<CDLootTableTable>("LootTable");
|
||||
|
||||
for (const auto& lootMatrix : lootMatrices) {
|
||||
int lootTableID = lootMatrix.LootTableIndex;
|
||||
std::vector<CDLootTable> vendorItems = lootTableTable->Query([=](CDLootTable entry) { return (entry.LootTableIndex == lootTableID); });
|
||||
if (lootMatrix.maxToDrop == 0 || lootMatrix.minToDrop == 0) {
|
||||
for (CDLootTable item : vendorItems) {
|
||||
m_Inventory.insert({item.itemid, item.sortPriority});
|
||||
}
|
||||
} else {
|
||||
auto randomCount = GeneralUtils::GenerateRandomNumber<int32_t>(lootMatrix.minToDrop, lootMatrix.maxToDrop);
|
||||
|
||||
for (size_t i = 0; i < randomCount; i++) {
|
||||
if (vendorItems.empty()) break;
|
||||
|
||||
auto randomItemIndex = GeneralUtils::GenerateRandomNumber<int32_t>(0, vendorItems.size() - 1);
|
||||
|
||||
const auto& randomItem = vendorItems[randomItemIndex];
|
||||
|
||||
vendorItems.erase(vendorItems.begin() + randomItemIndex);
|
||||
|
||||
m_Inventory.insert({randomItem.itemid, randomItem.sortPriority});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Because I want a vendor to sell these cameras
|
||||
if (m_Parent->GetLOT() == 13569) {
|
||||
auto randomCamera = GeneralUtils::GenerateRandomNumber<int32_t>(0, 2);
|
||||
|
||||
switch (randomCamera) {
|
||||
case 0:
|
||||
m_Inventory.insert({16253, 0}); //Grungagroid
|
||||
break;
|
||||
case 1:
|
||||
m_Inventory.insert({16254, 0}); //Hipstabrick
|
||||
break;
|
||||
case 2:
|
||||
m_Inventory.insert({16204, 0}); //Megabrixel snapshot
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Callback timer to refresh this inventory.
|
||||
m_Parent->AddCallbackTimer(m_RefreshTimeSeconds, [this]() {
|
||||
RefreshInventory();
|
||||
});
|
||||
GameMessages::SendVendorStatusUpdate(m_Parent, UNASSIGNED_SYSTEM_ADDRESS);
|
||||
}
|
||||
|
||||
void VendorComponent::SetupConstants() {
|
||||
auto* compRegistryTable = CDClientManager::Instance()->GetTable<CDComponentsRegistryTable>("ComponentsRegistry");
|
||||
int componentID = compRegistryTable->GetByIDAndType(m_Parent->GetLOT(), COMPONENT_TYPE_VENDOR);
|
||||
|
||||
auto* vendorComponentTable = CDClientManager::Instance()->GetTable<CDVendorComponentTable>("VendorComponent");
|
||||
std::vector<CDVendorComponent> vendorComps = vendorComponentTable->Query([=](CDVendorComponent entry) { return (entry.id == componentID); });
|
||||
if (vendorComps.empty()) return;
|
||||
m_BuyScalar = vendorComps[0].buyScalar;
|
||||
m_SellScalar = vendorComps[0].sellScalar;
|
||||
m_RefreshTimeSeconds = vendorComps[0].refreshTimeSeconds;
|
||||
m_LootMatrixID = vendorComps[0].LootMatrixIndex;
|
||||
}
|
@ -1,11 +1,12 @@
|
||||
#pragma once
|
||||
#ifndef VENDORCOMPONENT_H
|
||||
#define VENDORCOMPONENT_H
|
||||
|
||||
#include "RakNetTypes.h"
|
||||
#include "Entity.h"
|
||||
#include "GameMessages.h"
|
||||
#include "CDClientManager.h"
|
||||
#include "Component.h"
|
||||
#include "Entity.h"
|
||||
#include "GameMessages.h"
|
||||
#include "RakNetTypes.h"
|
||||
|
||||
/**
|
||||
* A component for vendor NPCs. A vendor sells items to the player.
|
||||
@ -56,17 +57,36 @@ public:
|
||||
*/
|
||||
std::map<LOT, int>& GetInventory();
|
||||
|
||||
/**
|
||||
* Refresh the inventory of this vendor.
|
||||
*/
|
||||
void RefreshInventory(bool isCreation = false);
|
||||
|
||||
/**
|
||||
* Called on startup of vendor to setup the variables for the component.
|
||||
*/
|
||||
void SetupConstants();
|
||||
private:
|
||||
/**
|
||||
* The buy scaler.
|
||||
* The buy scalar.
|
||||
*/
|
||||
float m_BuyScalar;
|
||||
|
||||
/**
|
||||
* The sell scaler.
|
||||
* The sell scalar.
|
||||
*/
|
||||
float m_SellScalar;
|
||||
|
||||
/**
|
||||
* The refresh time of this vendors' inventory.
|
||||
*/
|
||||
float m_RefreshTimeSeconds;
|
||||
|
||||
/**
|
||||
* Loot matrix id of this vendor.
|
||||
*/
|
||||
uint32_t m_LootMatrixID;
|
||||
|
||||
/**
|
||||
* The list of items the vendor sells.
|
||||
*/
|
||||
|
@ -285,7 +285,7 @@ void GameMessageHandler::HandleMessage(RakNet::BitStream* inStream, const System
|
||||
|
||||
auto* skillComponent = entity->GetComponent<SkillComponent>();
|
||||
|
||||
success = skillComponent->CastPlayerSkill(behaviorId, startSkill.uiSkillHandle, bs, startSkill.optionalTargetID);
|
||||
success = skillComponent->CastPlayerSkill(behaviorId, startSkill.uiSkillHandle, bs, startSkill.optionalTargetID, startSkill.skillID);
|
||||
|
||||
if (success && entity->GetCharacter()) {
|
||||
DestroyableComponent* destComp = entity->GetComponent<DestroyableComponent>();
|
||||
|
@ -1252,8 +1252,7 @@ void GameMessages::SendVendorOpenWindow(Entity* entity, const SystemAddress& sys
|
||||
SEND_PACKET
|
||||
}
|
||||
|
||||
// ah yes, impl code in a send function, beautiful!
|
||||
void GameMessages::SendVendorStatusUpdate(Entity* entity, const SystemAddress& sysAddr) {
|
||||
void GameMessages::SendVendorStatusUpdate(Entity* entity, const SystemAddress& sysAddr, bool bUpdateOnly) {
|
||||
CBITSTREAM
|
||||
CMSGHEADER
|
||||
|
||||
@ -1265,7 +1264,7 @@ void GameMessages::SendVendorStatusUpdate(Entity* entity, const SystemAddress& s
|
||||
bitStream.Write(entity->GetObjectID());
|
||||
bitStream.Write(GAME_MSG::GAME_MSG_VENDOR_STATUS_UPDATE);
|
||||
|
||||
bitStream.Write(false);
|
||||
bitStream.Write(bUpdateOnly);
|
||||
bitStream.Write(static_cast<uint32_t>(vendorItems.size()));
|
||||
|
||||
for (std::pair<LOT, int> item : vendorItems) {
|
||||
@ -1273,6 +1272,7 @@ void GameMessages::SendVendorStatusUpdate(Entity* entity, const SystemAddress& s
|
||||
bitStream.Write(static_cast<int>(item.second));
|
||||
}
|
||||
|
||||
if (sysAddr == UNASSIGNED_SYSTEM_ADDRESS) SEND_PACKET_BROADCAST
|
||||
SEND_PACKET
|
||||
}
|
||||
|
||||
|
@ -109,7 +109,7 @@ namespace GameMessages {
|
||||
void SendModularBuildEnd(Entity* entity);
|
||||
|
||||
void SendVendorOpenWindow(Entity* entity, const SystemAddress& sysAddr);
|
||||
void SendVendorStatusUpdate(Entity* entity, const SystemAddress& sysAddr);
|
||||
void SendVendorStatusUpdate(Entity* entity, const SystemAddress& sysAddr, bool bUpdateOnly = false);
|
||||
void SendVendorTransactionResult(Entity* entity, const SystemAddress& sysAddr);
|
||||
|
||||
void SendRemoveItemFromInventory(Entity* entity, const SystemAddress& sysAddr, LWOOBJID iObjID, LOT templateID, int inventoryType, uint32_t stackCount, uint32_t stackRemaining);
|
||||
|
@ -102,6 +102,10 @@ int32_t Inventory::FindEmptySlot()
|
||||
{
|
||||
newSize += 9u;
|
||||
}
|
||||
else
|
||||
{
|
||||
newSize += 10u;
|
||||
}
|
||||
|
||||
if (newSize > GetSize())
|
||||
{
|
||||
|
@ -326,10 +326,12 @@ void MissionTask::Progress(int32_t value, LWOOBJID associate, const std::string&
|
||||
|
||||
case MissionTaskType::MISSION_TASK_TYPE_SKILL:
|
||||
{
|
||||
if (!InParameters(value)) break;
|
||||
|
||||
AddProgress(count);
|
||||
|
||||
// This is a complicated check because for some missions we need to check for the associate being in the parameters instead of the value being in the parameters.
|
||||
if (associate == LWOOBJID_EMPTY && GetAllTargets().size() == 1 && GetAllTargets()[0] == -1) {
|
||||
if (InParameters(value)) AddProgress(count);
|
||||
} else {
|
||||
if (InParameters(associate) && InAllTargets(value)) AddProgress(count);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -324,6 +324,7 @@ enum GAME_MSG : unsigned short {
|
||||
GAME_MSG_ACTIVITY_STOP = 408,
|
||||
GAME_MSG_SHOOTING_GALLERY_CLIENT_AIM_UPDATE = 409,
|
||||
GAME_MSG_SHOOTING_GALLERY_FIRE = 411,
|
||||
GAME_MSG_REQUEST_VENDOR_STATUS_UPDATE = 416,
|
||||
GAME_MSG_VENDOR_STATUS_UPDATE = 417,
|
||||
GAME_MSG_NOTIFY_CLIENT_SHOOTING_GALLERY_SCORE = 425,
|
||||
GAME_MSG_CONSUME_CLIENT_ITEM = 427,
|
||||
|
@ -49,7 +49,7 @@ void BaseEnemyApe::OnTimerDone(Entity *self, std::string timerName) {
|
||||
if (destroyableComponent != nullptr) {
|
||||
destroyableComponent->SetArmor(destroyableComponent->GetMaxArmor() / timesStunned);
|
||||
}
|
||||
|
||||
EntityManager::Instance()->SerializeEntity(self);
|
||||
self->SetVar<uint32_t>(u"timesStunned", timesStunned + 1);
|
||||
StunApe(self, false);
|
||||
|
||||
|
@ -195,7 +195,7 @@ void NsConcertInstrument::EquipInstruments(Entity *self, Entity *player) {
|
||||
// Equip the left hand instrument
|
||||
const auto leftInstrumentLot = instrumentLotLeft.find(GetInstrumentLot(self))->second;
|
||||
if (leftInstrumentLot != LOT_NULL) {
|
||||
inventory->AddItem(leftInstrumentLot, 1, eLootSourceType::LOOT_SOURCE_ACTIVITY);
|
||||
inventory->AddItem(leftInstrumentLot, 1, eLootSourceType::LOOT_SOURCE_NONE, TEMP_ITEMS, {}, LWOOBJID_EMPTY, false);
|
||||
auto* leftInstrument = inventory->FindItemByLot(leftInstrumentLot, TEMP_ITEMS);
|
||||
leftInstrument->Equip();
|
||||
}
|
||||
@ -203,7 +203,7 @@ void NsConcertInstrument::EquipInstruments(Entity *self, Entity *player) {
|
||||
// Equip the right hand instrument
|
||||
const auto rightInstrumentLot = instrumentLotRight.find(GetInstrumentLot(self))->second;
|
||||
if (rightInstrumentLot != LOT_NULL) {
|
||||
inventory->AddItem(rightInstrumentLot, 1, eLootSourceType::LOOT_SOURCE_ACTIVITY);
|
||||
inventory->AddItem(rightInstrumentLot, 1, eLootSourceType::LOOT_SOURCE_NONE, TEMP_ITEMS, {}, LWOOBJID_EMPTY, false);
|
||||
auto* rightInstrument = inventory->FindItemByLot(rightInstrumentLot, TEMP_ITEMS);
|
||||
rightInstrument->Equip();
|
||||
}
|
||||
|
@ -38,10 +38,11 @@ void NtParadoxPanelServer::OnUse(Entity* self, Entity* user)
|
||||
GameMessages::SendPlayAnimation(player, u"rebuild-celebrate");
|
||||
|
||||
GameMessages::SendNotifyClientObject(self->GetObjectID(), u"SparkStop", 0, 0, player->GetObjectID(), "", player->GetSystemAddress());
|
||||
|
||||
GameMessages::SendSetStunned(player->GetObjectID(), eStunState::POP, player->GetSystemAddress(), LWOOBJID_EMPTY, false, false, true, false, true, true, false, false, true);
|
||||
self->SetVar(u"bActive", false);
|
||||
});
|
||||
|
||||
GameMessages::SendPlayAnimation(user, u"nexus-powerpanel", 6.0f);
|
||||
GameMessages::SendSetStunned(user->GetObjectID(), eStunState::PUSH, user->GetSystemAddress(), LWOOBJID_EMPTY, false, false, true, false, true, true, false, false, true);
|
||||
return;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user