mirror of
				https://github.com/DarkflameUniverse/DarkflameServer.git
				synced 2025-11-03 22:21:59 +00:00 
			
		
		
		
	Merge remote-tracking branch 'origin/main' into scripting-lua
This commit is contained in:
		@@ -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;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user