mirror of
https://github.com/DarkflameUniverse/DarkflameServer.git
synced 2025-01-08 22:07:10 +00:00
Updated how upgrade adds skills
This commit is contained in:
parent
87613f287f
commit
756dc4e44f
@ -7,8 +7,11 @@
|
||||
#include <InventoryComponent.h>
|
||||
#include <BaseCombatAIComponent.h>
|
||||
#include <TeamManager.h>
|
||||
#include <ControllablePhysicsComponent.h>
|
||||
#include <Item.h>
|
||||
|
||||
#include <queue>
|
||||
|
||||
using namespace nejlika;
|
||||
|
||||
float nejlika::AdditionalEntityData::CalculateModifier(ModifierType type, ModifierOperator op, bool resistance) const
|
||||
@ -117,6 +120,11 @@ float nejlika::AdditionalEntityData::CalculateResistance(ModifierType type) cons
|
||||
return CalculateModifier(type, ModifierOperator::Multiplicative, true);
|
||||
}
|
||||
|
||||
float nejlika::AdditionalEntityData::CalculateMultiplier(ModifierType type) const
|
||||
{
|
||||
return 1 + CalculateModifier(type, ModifierOperator::Multiplicative, false);
|
||||
}
|
||||
|
||||
std::vector<ModifierInstance> nejlika::AdditionalEntityData::TriggerUpgradeItems(UpgradeTriggerType triggerType) {
|
||||
auto* entity = Game::entityManager->GetEntity(id);
|
||||
|
||||
@ -155,6 +163,99 @@ std::vector<ModifierInstance> nejlika::AdditionalEntityData::TriggerUpgradeItems
|
||||
return result;
|
||||
}
|
||||
|
||||
void nejlika::AdditionalEntityData::InitializeSkills() {
|
||||
auto* entity = Game::entityManager->GetEntity(id);
|
||||
|
||||
if (entity == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto* inventoryComponent = entity->GetComponent<InventoryComponent>();
|
||||
|
||||
if (inventoryComponent == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
struct entry {
|
||||
LWOOBJID id;
|
||||
int32_t priority;
|
||||
|
||||
entry(LWOOBJID id, int32_t priority) : id(id), priority(priority) {}
|
||||
};
|
||||
|
||||
std::vector<entry> items;
|
||||
|
||||
for (const auto& itemID : upgradeItems) {
|
||||
auto* item = inventoryComponent->FindItemById(itemID);
|
||||
|
||||
if (item == nullptr) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto priority = item->GetSlot();
|
||||
|
||||
items.push_back(entry(itemID, priority));
|
||||
}
|
||||
|
||||
std::sort(items.begin(), items.end(), [](const entry& a, const entry& b) {
|
||||
return a.priority < b.priority;
|
||||
});
|
||||
|
||||
for (const auto& item : items) {
|
||||
AddSkills(item.id);
|
||||
}
|
||||
}
|
||||
|
||||
void nejlika::AdditionalEntityData::AddSkills(LWOOBJID item) {
|
||||
if (!upgradeItems.contains(item)) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto* entity = Game::entityManager->GetEntity(id);
|
||||
|
||||
if (entity == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto* inventoryComponent = entity->GetComponent<InventoryComponent>();
|
||||
|
||||
if (inventoryComponent == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto* itemData = inventoryComponent->FindItemById(item);
|
||||
|
||||
if (itemData == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto upgradeDataOpt = NejlikaData::GetUpgradeTemplate(itemData->GetLot());
|
||||
|
||||
if (!upgradeDataOpt.has_value()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto& upgradeData = *upgradeDataOpt.value();
|
||||
|
||||
LOG("Adding skills for item %i", id);
|
||||
|
||||
upgradeData.AddSkills(id);
|
||||
}
|
||||
|
||||
void nejlika::AdditionalEntityData::RemoveSkills(LOT lot) {
|
||||
const auto upgradeDataOpt = NejlikaData::GetUpgradeTemplate(lot);
|
||||
|
||||
if (!upgradeDataOpt.has_value()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto& upgradeData = *upgradeDataOpt.value();
|
||||
|
||||
LOG("Removing skills for item %i", id);
|
||||
|
||||
upgradeData.RemoveSkills(id);
|
||||
}
|
||||
|
||||
void nejlika::AdditionalEntityData::RollStandardModifiers(int32_t level) {
|
||||
standardModifiers.clear();
|
||||
|
||||
@ -252,6 +353,11 @@ void nejlika::AdditionalEntityData::ApplyToEntity() {
|
||||
destroyable->SetImagination(destroyable->GetMaxImagination());
|
||||
}
|
||||
|
||||
if (entity->IsPlayer()) {
|
||||
auto* controllablePhysicsComponent = entity->GetComponent<ControllablePhysicsComponent>();
|
||||
if (controllablePhysicsComponent) controllablePhysicsComponent->SetSpeedMultiplier(CalculateMultiplier(ModifierType::Speed));
|
||||
}
|
||||
|
||||
initialized = true;
|
||||
}
|
||||
|
||||
|
@ -33,6 +33,14 @@ public:
|
||||
|
||||
float CalculateResistance(ModifierType type) const;
|
||||
|
||||
/**
|
||||
* @brief Calculate the multiplier for a given modifier type. With a base value of 1.0.
|
||||
*
|
||||
* @param type The modifier type.
|
||||
* @return The multiplier.
|
||||
*/
|
||||
float CalculateMultiplier(ModifierType type) const;
|
||||
|
||||
void ApplyToEntity();
|
||||
|
||||
void CheckForRescale(AdditionalEntityData* other);
|
||||
@ -51,6 +59,11 @@ public:
|
||||
|
||||
std::vector<ModifierInstance> TriggerUpgradeItems(UpgradeTriggerType triggerType);
|
||||
|
||||
void InitializeSkills();
|
||||
|
||||
void AddSkills(LWOOBJID item);
|
||||
void RemoveSkills(LOT lot);
|
||||
|
||||
private:
|
||||
void RollStandardModifiers(int32_t level);
|
||||
|
||||
|
@ -21,6 +21,7 @@ set(DGAME_SOURCES "Character.cpp"
|
||||
"NejlikaHooks.cpp"
|
||||
"UpgradeTemplate.cpp"
|
||||
"UpgradeEffect.cpp"
|
||||
"Lookup.cpp"
|
||||
)
|
||||
|
||||
include_directories(
|
||||
|
@ -97,6 +97,7 @@
|
||||
#include "CDZoneTableTable.h"
|
||||
|
||||
Observable<Entity*, const PositionUpdate&> Entity::OnPlayerPositionUpdate;
|
||||
Observable<Entity*> Entity::OnReadyForUpdates;
|
||||
|
||||
Entity::Entity(const LWOOBJID& objectID, EntityInfo info, User* parentUser, Entity* parentEntity) {
|
||||
m_ObjectID = objectID;
|
||||
@ -1780,6 +1781,12 @@ void Entity::SetOwnerOverride(const LWOOBJID value) {
|
||||
m_OwnerOverride = value;
|
||||
}
|
||||
|
||||
void Entity::SetPlayerReadyForUpdates() {
|
||||
m_PlayerIsReadyForUpdates = true;
|
||||
|
||||
OnReadyForUpdates(this);
|
||||
}
|
||||
|
||||
bool Entity::GetIsGhostingCandidate() const {
|
||||
return m_IsGhostingCandidate;
|
||||
}
|
||||
|
@ -117,7 +117,7 @@ public:
|
||||
|
||||
void SetOwnerOverride(LWOOBJID value);
|
||||
|
||||
void SetPlayerReadyForUpdates() { m_PlayerIsReadyForUpdates = true; }
|
||||
void SetPlayerReadyForUpdates();
|
||||
|
||||
void SetObservers(int8_t value);
|
||||
|
||||
@ -304,6 +304,8 @@ public:
|
||||
* @brief The observable for player entity position updates.
|
||||
*/
|
||||
static Observable<Entity*, const PositionUpdate&> OnPlayerPositionUpdate;
|
||||
|
||||
static Observable<Entity*> OnReadyForUpdates;
|
||||
|
||||
protected:
|
||||
LWOOBJID m_ObjectID;
|
||||
|
181
dGame/Lookup.cpp
Normal file
181
dGame/Lookup.cpp
Normal file
@ -0,0 +1,181 @@
|
||||
#include "Lookup.h"
|
||||
|
||||
#include <fstream>
|
||||
|
||||
#include "json.hpp"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
using namespace nejlika;
|
||||
|
||||
Lookup::Lookup(const std::filesystem::path& lookup)
|
||||
{
|
||||
m_Lookup = lookup;
|
||||
|
||||
// Check if the file exists.
|
||||
if (!std::filesystem::exists(lookup))
|
||||
{
|
||||
// Empty lookup
|
||||
return;
|
||||
}
|
||||
|
||||
// Read the json file
|
||||
std::ifstream file(lookup);
|
||||
|
||||
std::string json((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
|
||||
|
||||
// Parse the json
|
||||
auto doc = nlohmann::json::parse(json);
|
||||
|
||||
|
||||
// The document is a map, so we can iterate over it.
|
||||
for (const auto& [key, v] : doc.items()) {
|
||||
if (v.is_number_integer())
|
||||
{
|
||||
m_LookupMap[key] = v.get<int64_t>();
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!v.is_object()) {
|
||||
std::stringstream ss;
|
||||
|
||||
ss << "Invalid value for key \"" << key << "\" in lookup.";
|
||||
|
||||
throw std::runtime_error(ss.str());
|
||||
}
|
||||
|
||||
// Get the data
|
||||
auto data = v.get<nlohmann::json>();
|
||||
|
||||
// Get the id
|
||||
auto id = data["id"].get<int64_t>();
|
||||
|
||||
m_LookupMap[key] = id;
|
||||
|
||||
// Get the metadata
|
||||
auto metadata = data["metadata"].get<std::string>();
|
||||
|
||||
m_Metadata[key] = metadata;
|
||||
}
|
||||
}
|
||||
|
||||
nejlika::Lookup::Lookup(const Lookup &other)
|
||||
{
|
||||
m_Lookup = other.m_Lookup;
|
||||
m_LookupMap = other.m_LookupMap;
|
||||
m_Metadata = other.m_Metadata;
|
||||
m_CoreSymbols = other.m_CoreSymbols;
|
||||
}
|
||||
|
||||
id Lookup::GetValue(const name& symbol) const
|
||||
{
|
||||
id value;
|
||||
|
||||
if (IsCoreSymbol(symbol, value)) {
|
||||
return value;
|
||||
}
|
||||
|
||||
const auto& it = m_LookupMap.find(symbol);
|
||||
|
||||
if (it == m_LookupMap.end())
|
||||
{
|
||||
std::stringstream ss;
|
||||
|
||||
ss << "Symbol \"" << symbol << "\" does not exist in the lookup.";
|
||||
|
||||
throw std::runtime_error(ss.str());
|
||||
}
|
||||
|
||||
return it->second;
|
||||
}
|
||||
|
||||
bool nejlika::Lookup::Exists(const name &symbol) const
|
||||
{
|
||||
return IsCoreSymbol(symbol) || (m_CoreSymbols.find(symbol) != m_CoreSymbols.end()) || (m_LookupMap.find(symbol) != m_LookupMap.end());
|
||||
}
|
||||
|
||||
bool nejlika::Lookup::Exists(id value) const
|
||||
{
|
||||
for (const auto& [k, v] : m_LookupMap)
|
||||
{
|
||||
if (v == value)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
const std::string& Lookup::GetMetadata(const name& symbol) const
|
||||
{
|
||||
const auto& it = m_Metadata.find(symbol);
|
||||
|
||||
if (it == m_Metadata.end())
|
||||
{
|
||||
static std::string empty;
|
||||
|
||||
return empty;
|
||||
}
|
||||
|
||||
return it->second;
|
||||
}
|
||||
|
||||
const std::filesystem::path& Lookup::GetLookup() const
|
||||
{
|
||||
return m_Lookup;
|
||||
}
|
||||
|
||||
const std::unordered_map<name, id> &nejlika::Lookup::GetMap() const
|
||||
{
|
||||
return m_LookupMap;
|
||||
}
|
||||
|
||||
bool nejlika::Lookup::IsCoreSymbol(const name &symbol, id &value) const
|
||||
{
|
||||
try {
|
||||
value = std::stoi(symbol);
|
||||
|
||||
return true;
|
||||
} catch (...) {
|
||||
// cont...
|
||||
}
|
||||
|
||||
if (!symbol.starts_with(core_prefix)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check in the core symbols
|
||||
const auto& it = m_CoreSymbols.find(symbol);
|
||||
|
||||
if (it != m_CoreSymbols.end())
|
||||
{
|
||||
value = it->second;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// In the format "core:<value>"
|
||||
try {
|
||||
value = std::stoi(symbol.substr(core_prefix.size() + 1));
|
||||
} catch (...) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool nejlika::Lookup::IsCoreSymbol(const name &symbol)
|
||||
{
|
||||
// Check if it can be converted to an integer
|
||||
try {
|
||||
[[maybe_unused]] auto value = std::stoi(symbol);
|
||||
|
||||
return true;
|
||||
} catch (...) {
|
||||
// cont...
|
||||
}
|
||||
|
||||
return symbol.starts_with(core_prefix);
|
||||
}
|
117
dGame/Lookup.h
Normal file
117
dGame/Lookup.h
Normal file
@ -0,0 +1,117 @@
|
||||
#pragma once
|
||||
|
||||
#include <filesystem>
|
||||
#include <functional>
|
||||
#include <cstdint>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
|
||||
namespace nejlika {
|
||||
|
||||
typedef std::string name;
|
||||
typedef int id;
|
||||
|
||||
/**
|
||||
* @brief A one-way mapping between symbols (names) and their corresponding numerical values.
|
||||
*
|
||||
* This is an active lookup, meaning that it is possible to add new symbols and their values to the lookup.
|
||||
*
|
||||
* A method is provided to wait for a symbol to be added to the lookup.
|
||||
*/
|
||||
class Lookup
|
||||
{
|
||||
public:
|
||||
inline static const name core_prefix = "lego-universe";
|
||||
|
||||
/**
|
||||
* @brief Constructs a Lookup object with the specified lookup file path.
|
||||
*
|
||||
* @param lookup The path to the lookup file.
|
||||
* @throw If the lookup file could not be parsed.
|
||||
*/
|
||||
Lookup(const std::filesystem::path& lookup);
|
||||
|
||||
Lookup(const Lookup& other);
|
||||
|
||||
Lookup() = default;
|
||||
|
||||
/**
|
||||
* @brief Gets the value of the specified symbol.
|
||||
*
|
||||
* @param symbol The symbol to get the value of.
|
||||
* @return The value of the specified symbol.
|
||||
* @throw If the specified symbol does not exist.
|
||||
*/
|
||||
id GetValue(const name& symbol) const;
|
||||
|
||||
/**
|
||||
* @brief Checks whether the specified symbol exists.
|
||||
*
|
||||
* @param symbol The symbol to check.
|
||||
* @return Whether the specified symbol exists.
|
||||
*/
|
||||
bool Exists(const name& symbol) const;
|
||||
|
||||
/**
|
||||
* @brief Checks whether any symbol has the specified value.
|
||||
*
|
||||
* @param value The value to check.
|
||||
* @return Whether any symbol has the specified value.
|
||||
*/
|
||||
bool Exists(id value) const;
|
||||
|
||||
/**
|
||||
* @brief Gets the metadata of a specified symbol.
|
||||
*
|
||||
* @param symbol The symbol to get metadata of.
|
||||
* @return The metadata of the specified symbol or an empty string if the symbol does not exist.
|
||||
*/
|
||||
const std::string& GetMetadata(const name& symbol) const;
|
||||
|
||||
/**
|
||||
* @brief Gets the path to the lookup file.
|
||||
*
|
||||
* @return The path to the lookup file.
|
||||
*/
|
||||
const std::filesystem::path& GetLookup() const;
|
||||
|
||||
/**
|
||||
* @brief Gets the map of all symbols and their values.
|
||||
*
|
||||
* @return The map of all symbols and their values.
|
||||
*/
|
||||
const std::unordered_map<name, id>& GetMap() const;
|
||||
|
||||
/**
|
||||
* @brief Checks whether the specified symbol is a core symbol.
|
||||
*
|
||||
* @param symbol The symbol to check.
|
||||
* @param value The value of the core symbol.
|
||||
* @return Whether the specified symbol is a core symbol.
|
||||
*/
|
||||
bool IsCoreSymbol(const name& symbol, id& value) const;
|
||||
|
||||
/**
|
||||
* @brief Checks whether the specified symbol is a core symbol.
|
||||
*
|
||||
* A symbol is considered a core symbol if it is either:
|
||||
* a number;
|
||||
* or a string starting with the core_prefix.
|
||||
*
|
||||
* @param symbol The symbol to check.
|
||||
* @return Whether the specified symbol is a core symbol.
|
||||
*/
|
||||
static bool IsCoreSymbol(const name& symbol);
|
||||
|
||||
private:
|
||||
|
||||
std::filesystem::path m_Lookup;
|
||||
|
||||
std::unordered_map<name, id> m_LookupMap;
|
||||
|
||||
std::unordered_map<name, std::string> m_Metadata;
|
||||
|
||||
std::unordered_map<name, id> m_CoreSymbols;
|
||||
};
|
||||
|
||||
} // namespace nejlika
|
@ -24,10 +24,16 @@ enum class ModifierType : uint8_t
|
||||
Lightning,
|
||||
Corruption,
|
||||
|
||||
Elemental,
|
||||
|
||||
Psychic,
|
||||
|
||||
Damage,
|
||||
|
||||
Speed,
|
||||
|
||||
AttackSpeed,
|
||||
|
||||
Invalid
|
||||
};
|
||||
|
||||
|
@ -4,6 +4,7 @@
|
||||
#include "dConfig.h"
|
||||
#include "json.hpp"
|
||||
#include "Logger.h"
|
||||
#include "Lookup.h"
|
||||
|
||||
#include <fstream>
|
||||
|
||||
@ -22,6 +23,13 @@ std::unordered_map<LOT, nejlika::EntityTemplate> entityTemplates;
|
||||
|
||||
std::unordered_map<LOT, nejlika::UpgradeTemplate> upgradeTemplates;
|
||||
|
||||
nejlika::Lookup lookup;
|
||||
|
||||
}
|
||||
|
||||
const nejlika::Lookup& nejlika::NejlikaData::GetLookup()
|
||||
{
|
||||
return lookup;
|
||||
}
|
||||
|
||||
const std::unordered_map<ModifierNameType,std::vector<ModifierNameTemplate>>& nejlika::NejlikaData::GetModifierNameTemplates()
|
||||
@ -171,5 +179,12 @@ void nejlika::NejlikaData::LoadNejlikaData()
|
||||
upgradeTemplates[upgradeTemplate.GetLot()] = upgradeTemplate;
|
||||
}
|
||||
}
|
||||
|
||||
const auto& lookupFile = Game::config->GetValue("lookup");
|
||||
|
||||
if (!lookupFile.empty())
|
||||
{
|
||||
lookup = Lookup(lookupFile);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -8,6 +8,7 @@
|
||||
#include "AdditionalItemData.h"
|
||||
#include "AdditionalEntityData.h"
|
||||
#include "UpgradeTemplate.h"
|
||||
#include "Lookup.h"
|
||||
|
||||
namespace nejlika::NejlikaData
|
||||
{
|
||||
@ -34,4 +35,6 @@ void UnsetAdditionalEntityData(LWOOBJID id);
|
||||
|
||||
void LoadNejlikaData();
|
||||
|
||||
const Lookup& GetLookup();
|
||||
|
||||
}
|
||||
|
@ -18,6 +18,9 @@
|
||||
#include <eGameMessageType.h>
|
||||
#include <dServer.h>
|
||||
#include <Item.h>
|
||||
#include <SkillComponent.h>
|
||||
#include <MissionComponent.h>
|
||||
#include <eMissionState.h>
|
||||
|
||||
#include "NejlikaData.h"
|
||||
|
||||
@ -91,8 +94,7 @@ void nejlika::NejlikaHooks::InstallHooks()
|
||||
auto& upgradeTemplate = *upgradeTemplateOpt.value();
|
||||
|
||||
entityData.AddUpgradeItem(item->GetId());
|
||||
|
||||
entityData.TriggerUpgradeItems(UpgradeTriggerType::Equip);
|
||||
entityData.AddSkills(item->GetId());
|
||||
};
|
||||
|
||||
EntityManager::OnEntityCreated += [](Entity* entity) {
|
||||
@ -137,7 +139,26 @@ void nejlika::NejlikaHooks::InstallHooks()
|
||||
}
|
||||
}
|
||||
|
||||
additionalData.TriggerUpgradeItems(UpgradeTriggerType::Equip);
|
||||
additionalData.InitializeSkills();
|
||||
};
|
||||
|
||||
Entity::OnReadyForUpdates += [](Entity* entity) {
|
||||
if (!entity->IsPlayer())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
GameMessages::SendAddSkill(entity, NejlikaData::GetLookup().GetValue("intro:skills:proxy:main"), BehaviorSlot::Head);
|
||||
GameMessages::SendAddSkill(entity, NejlikaData::GetLookup().GetValue("intro:skills:proxy:secondary"), BehaviorSlot::Offhand);
|
||||
GameMessages::SendAddSkill(entity, NejlikaData::GetLookup().GetValue("intro:skills:proxy:tertiary"), BehaviorSlot::Neck);
|
||||
|
||||
auto* missionComponent = entity->GetComponent<MissionComponent>();
|
||||
|
||||
if (missionComponent) {
|
||||
if (missionComponent->GetMissionState(1732) != eMissionState::COMPLETE) {
|
||||
missionComponent->CompleteMission(1732, true, false);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
EntityManager::OnEntityDestroyed += [](Entity* entity) {
|
||||
@ -149,17 +170,18 @@ void nejlika::NejlikaHooks::InstallHooks()
|
||||
};
|
||||
|
||||
InventoryComponent::OnItemDestroyed += [](InventoryComponent* component, Item* item) {
|
||||
UnsetAdditionalItemData(item->GetId());
|
||||
|
||||
auto entityDataOpt = GetAdditionalEntityData(component->GetParent()->GetObjectID());
|
||||
|
||||
if (!entityDataOpt.has_value()) {
|
||||
UnsetAdditionalItemData(item->GetId());
|
||||
return;
|
||||
}
|
||||
|
||||
auto& entityData = *entityDataOpt.value();
|
||||
|
||||
entityData.RemoveUpgradeItem(item->GetId());
|
||||
entityData.RemoveSkills(item->GetLot());
|
||||
UnsetAdditionalItemData(item->GetId());
|
||||
};
|
||||
|
||||
LevelProgressionComponent::OnLevelUp += [](LevelProgressionComponent* component) {
|
||||
@ -181,7 +203,7 @@ void nejlika::NejlikaHooks::InstallHooks()
|
||||
return;
|
||||
}
|
||||
|
||||
inventoryComponent->AddItem(2097253, 1, eLootSourceType::LEVEL_REWARD);
|
||||
inventoryComponent->AddItem(NejlikaData::GetLookup().GetValue("intro:upgrades:level-token"), 1, eLootSourceType::MODERATION);
|
||||
};
|
||||
|
||||
InventoryComponent::OnItemEquipped += [](InventoryComponent* component, Item* item) {
|
||||
@ -243,6 +265,147 @@ void nejlika::NejlikaHooks::InstallHooks()
|
||||
entityData.ApplyToEntity();
|
||||
};
|
||||
|
||||
SkillComponent::OnSkillCast += [](SkillComponent* skillComponent, uint32_t skillID, bool success) {
|
||||
std::cout << "Skill cast: " << skillID << " - " << success << std::endl;
|
||||
|
||||
auto* inventoryComponent = skillComponent->GetParent()->GetComponent<InventoryComponent>();
|
||||
|
||||
if (!inventoryComponent) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto* entity = skillComponent->GetParent();
|
||||
|
||||
auto skills = inventoryComponent->GetSkills();
|
||||
|
||||
const auto primaryTrigger = NejlikaData::GetLookup().GetValue("intro:skills:proxy:main");
|
||||
const auto secondaryTrigger = NejlikaData::GetLookup().GetValue("intro:skills:proxy:secondary");
|
||||
const auto tertiaryTrigger = NejlikaData::GetLookup().GetValue("intro:skills:proxy:tertiary");
|
||||
|
||||
if (skillID == primaryTrigger || skillID == secondaryTrigger || skillID == tertiaryTrigger) {
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
const auto primarySkills = skills[BehaviorSlot::Primary];
|
||||
|
||||
// If the skillID is in the primary skills, ignore this
|
||||
if (primarySkills.contains(skillID)) {
|
||||
if (entity->HasVar(u"skill-cast") && entity->GetVar<size_t>(u"skill-cast") == 0) {
|
||||
GameMessages::SendAddSkill(entity, primaryTrigger, BehaviorSlot::Head);
|
||||
GameMessages::SendAddSkill(entity, secondaryTrigger, BehaviorSlot::Offhand);
|
||||
GameMessages::SendAddSkill(entity, tertiaryTrigger, BehaviorSlot::Neck);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (entity->HasVar(u"skill-cast")) {
|
||||
entity->SetVar(u"skill-cast", static_cast<size_t>(0));
|
||||
}
|
||||
|
||||
if (entity->HasVar(u"skill-cast-slot")) {
|
||||
BehaviorSlot slot = static_cast<BehaviorSlot>(entity->GetVar<int32_t>(u"skill-cast-slot"));
|
||||
|
||||
auto primarySkills = inventoryComponent->GetSkills();
|
||||
|
||||
for (const auto& skill : primarySkills[slot]) {
|
||||
GameMessages::SendRemoveSkill(entity, skill);
|
||||
}
|
||||
}
|
||||
|
||||
LOG("Restoring triggers via skill");
|
||||
|
||||
// Restore the triggers
|
||||
GameMessages::SendAddSkill(entity, primaryTrigger, BehaviorSlot::Head);
|
||||
GameMessages::SendAddSkill(entity, secondaryTrigger, BehaviorSlot::Offhand);
|
||||
GameMessages::SendAddSkill(entity, tertiaryTrigger, BehaviorSlot::Neck);
|
||||
*/
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
static const std::vector<BehaviorSlot> slotOrder = {
|
||||
BehaviorSlot::Head,
|
||||
BehaviorSlot::Offhand,
|
||||
BehaviorSlot::Neck
|
||||
};
|
||||
|
||||
std::set<uint32_t> selectedSkills;
|
||||
BehaviorSlot slot = BehaviorSlot::Invalid;
|
||||
|
||||
if (skillID == primaryTrigger) {
|
||||
selectedSkills = skills[BehaviorSlot::Head];
|
||||
slot = BehaviorSlot::Head;
|
||||
} else if (skillID == secondaryTrigger) {
|
||||
selectedSkills = skills[BehaviorSlot::Offhand];
|
||||
slot = BehaviorSlot::Offhand;
|
||||
} else if (skillID == tertiaryTrigger) {
|
||||
selectedSkills = skills[BehaviorSlot::Neck];
|
||||
slot = BehaviorSlot::Neck;
|
||||
}
|
||||
|
||||
if (selectedSkills.empty()) {
|
||||
return;
|
||||
}
|
||||
else {
|
||||
GameMessages::SendRemoveSkill(entity, primaryTrigger);
|
||||
GameMessages::SendRemoveSkill(entity, secondaryTrigger);
|
||||
GameMessages::SendRemoveSkill(entity, tertiaryTrigger);
|
||||
}
|
||||
|
||||
int32_t i = 0;
|
||||
for (const auto& skill : selectedSkills) {
|
||||
if (i >= 3) {
|
||||
break;
|
||||
}
|
||||
|
||||
GameMessages::SendAddSkill(entity, skill, slotOrder[i]);
|
||||
i++;
|
||||
}
|
||||
|
||||
const auto randomNumber = GeneralUtils::GenerateRandomNumber<size_t>();
|
||||
|
||||
entity->SetVar(u"skill-cast", randomNumber);
|
||||
entity->SetVar(u"skill-cast-slot", static_cast<int32_t>(slot));
|
||||
|
||||
entity->AddCallbackTimer(1.0f, [entity, randomNumber, primaryTrigger, secondaryTrigger, tertiaryTrigger]() {
|
||||
if (!entity->HasVar(u"skill-cast")) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto currentRandom = entity->GetVar<size_t>(u"skill-cast");
|
||||
|
||||
if (currentRandom != randomNumber) {
|
||||
return;
|
||||
}
|
||||
|
||||
entity->SetVar(u"skill-cast", static_cast<size_t>(0));
|
||||
|
||||
LOG("Restoring triggers via timeout");
|
||||
|
||||
// Remove the skills
|
||||
auto* inventoryComponent = entity->GetComponent<InventoryComponent>();
|
||||
|
||||
if (!inventoryComponent) {
|
||||
return;
|
||||
}
|
||||
|
||||
BehaviorSlot slot = static_cast<BehaviorSlot>(entity->GetVar<int32_t>(u"skill-cast-slot"));
|
||||
|
||||
auto primarySkills = inventoryComponent->GetSkills();
|
||||
|
||||
for (const auto& skill : primarySkills[slot]) {
|
||||
GameMessages::SendRemoveSkill(entity, skill);
|
||||
}
|
||||
|
||||
// Restore the triggers
|
||||
GameMessages::SendAddSkill(entity, primaryTrigger, BehaviorSlot::Head);
|
||||
GameMessages::SendAddSkill(entity, secondaryTrigger, BehaviorSlot::Offhand);
|
||||
GameMessages::SendAddSkill(entity, tertiaryTrigger, BehaviorSlot::Neck);
|
||||
});
|
||||
};
|
||||
|
||||
DestroyableComponent::OnDamageCalculation += [](Entity* damaged, LWOOBJID offender, uint32_t skillID, uint32_t& damage) {
|
||||
std::cout << "Calculating damage with skill: " << skillID << std::endl;
|
||||
|
||||
@ -299,7 +462,14 @@ void nejlika::NejlikaHooks::InstallHooks()
|
||||
std::cout << "Found " << skills.size() << " skills." << std::endl;
|
||||
|
||||
// omg...
|
||||
for (const auto& [slot, skill] : skills) {
|
||||
for (const auto& [slot, skillSet] : skills) {
|
||||
if (skillSet.empty())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto& skill = *skillSet.begin();
|
||||
|
||||
std::cout << "Found skill: " << skill << std::endl;
|
||||
|
||||
if (skill != skillID) {
|
||||
@ -372,6 +542,8 @@ void nejlika::NejlikaHooks::InstallHooks()
|
||||
damageTypes.erase(ModifierType::Armor);
|
||||
damageTypes.erase(ModifierType::Imagination);
|
||||
damageTypes.erase(ModifierType::Damage);
|
||||
damageTypes.erase(ModifierType::Speed);
|
||||
damageTypes.erase(ModifierType::AttackSpeed);
|
||||
damageTypes.erase(ModifierType::Invalid);
|
||||
|
||||
uint32_t totalDamage = 0;
|
||||
@ -441,8 +613,10 @@ void nejlika::NejlikaHooks::InstallHooks()
|
||||
|
||||
damage = totalDamage;
|
||||
|
||||
auto attackSpeed = offenderEntity.CalculateModifier(ModifierType::AttackSpeed, modifiers, level);
|
||||
|
||||
if (offfendEntity->IsPlayer()) {
|
||||
offfendEntity->AddCallbackTimer(0.0f, [offfendEntity, skillID]() {
|
||||
offfendEntity->AddCallbackTimer(0.0f, [offfendEntity, skillID, attackSpeed]() {
|
||||
CBITSTREAM;
|
||||
CMSGHEADER;
|
||||
|
||||
@ -452,7 +626,7 @@ void nejlika::NejlikaHooks::InstallHooks()
|
||||
bitStream.Write(eGameMessageType::MODIFY_SKILL_COOLDOWN);
|
||||
|
||||
bitStream.Write1();
|
||||
bitStream.Write<float>(-10.0f);
|
||||
bitStream.Write<float>(attackSpeed);
|
||||
bitStream.Write<int32_t>(static_cast<int32_t>(skillID));
|
||||
|
||||
LOG("Sending cooldown reduction for skill: %d", skillID);
|
||||
|
@ -63,14 +63,6 @@ nlohmann::json nejlika::UpgradeEffect::ToJson() const
|
||||
json["grant-skill-id"] = equipSkillID;
|
||||
}
|
||||
|
||||
if (equipSkillSlot != BehaviorSlot::Invalid) {
|
||||
json["grant-skill-slot"] = magic_enum::enum_name(equipSkillSlot);
|
||||
}
|
||||
|
||||
if (unequipSkill) {
|
||||
json["unequip-skill"] = true;
|
||||
}
|
||||
|
||||
return json;
|
||||
}
|
||||
|
||||
@ -130,14 +122,6 @@ void nejlika::UpgradeEffect::Load(const nlohmann::json& json)
|
||||
if (json.contains("grant-skill-id")) {
|
||||
equipSkillID = json["grant-skill-id"].get<int32_t>();
|
||||
}
|
||||
|
||||
if (json.contains("grant-skill-slot")) {
|
||||
equipSkillSlot = magic_enum::enum_cast<BehaviorSlot>(json["grant-skill-slot"].get<std::string>()).value_or(BehaviorSlot::Invalid);
|
||||
}
|
||||
|
||||
if (json.contains("unequip-skill")) {
|
||||
unequipSkill = json["unequip-skill"].get<bool>();
|
||||
}
|
||||
}
|
||||
|
||||
float nejlika::UpgradeEffect::CalculateChance(int32_t level) const {
|
||||
@ -227,16 +211,6 @@ void nejlika::UpgradeEffect::OnTrigger(LWOOBJID origin) const {
|
||||
if (!inventory) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (equipSkillID != 0) {
|
||||
std::cout << "Granting skill: " << equipSkillID << " to entity: " << origin << " in slot: " << magic_enum::enum_name(equipSkillSlot) << std::endl;
|
||||
inventory->SetSkill(equipSkillSlot, equipSkillID);
|
||||
}
|
||||
|
||||
if (unequipSkill) {
|
||||
std::cout << "Unequipping skill from entity: " << origin << " in slot: " << magic_enum::enum_name(equipSkillSlot) << std::endl;
|
||||
inventory->ResetSkill(equipSkillSlot);
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<ModifierInstance> nejlika::UpgradeEffect::Trigger(const std::vector<UpgradeEffect>& modifiers, int32_t level, UpgradeTriggerType triggerType, LWOOBJID origin) {
|
||||
@ -279,3 +253,47 @@ std::vector<ModifierInstance> nejlika::UpgradeEffect::Trigger(const std::vector<
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void nejlika::UpgradeEffect::AddSkill(LWOOBJID origin) const {
|
||||
auto* entity = Game::entityManager->GetEntity(origin);
|
||||
|
||||
if (!entity) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto* inventory = entity->GetComponent<InventoryComponent>();
|
||||
|
||||
if (!inventory) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (triggerType != UpgradeTriggerType::Active) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (equipSkillID != 0) {
|
||||
inventory->SetSkill(equipSkillID);
|
||||
}
|
||||
}
|
||||
|
||||
void nejlika::UpgradeEffect::RemoveSkill(LWOOBJID origin) const {
|
||||
auto* entity = Game::entityManager->GetEntity(origin);
|
||||
|
||||
if (!entity) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto* inventory = entity->GetComponent<InventoryComponent>();
|
||||
|
||||
if (!inventory) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (triggerType != UpgradeTriggerType::Active) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (equipSkillID != 0) {
|
||||
inventory->UnsetSkill(equipSkillID);
|
||||
}
|
||||
}
|
||||
|
@ -35,6 +35,9 @@ public:
|
||||
|
||||
UpgradeTriggerType GetTriggerType() const { return triggerType; }
|
||||
|
||||
void AddSkill(LWOOBJID origin) const;
|
||||
void RemoveSkill(LWOOBJID origin) const;
|
||||
|
||||
private:
|
||||
struct UpgradeScale
|
||||
{
|
||||
@ -46,8 +49,6 @@ private:
|
||||
std::vector<UpgradeTriggerCondition> conditions;
|
||||
UpgradeTriggerType triggerType;
|
||||
int32_t equipSkillID = 0;
|
||||
bool unequipSkill = false;
|
||||
BehaviorSlot equipSkillSlot = BehaviorSlot::Invalid;
|
||||
std::vector<ModifierTemplate> modifiers;
|
||||
int32_t effectID = 0;
|
||||
std::string effectType = "";
|
||||
|
@ -46,4 +46,15 @@ std::vector<ModifierInstance> nejlika::UpgradeTemplate::Trigger(int32_t level, U
|
||||
return UpgradeEffect::Trigger(passives, level, triggerType, origin);
|
||||
}
|
||||
|
||||
void nejlika::UpgradeTemplate::AddSkills(LWOOBJID origin) const {
|
||||
for (const auto& passive : passives) {
|
||||
passive.AddSkill(origin);
|
||||
}
|
||||
}
|
||||
|
||||
void nejlika::UpgradeTemplate::RemoveSkills(LWOOBJID origin) const {
|
||||
for (const auto& passive : passives) {
|
||||
passive.RemoveSkill(origin);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -29,6 +29,9 @@ public:
|
||||
|
||||
std::vector<ModifierInstance> Trigger(int32_t level, UpgradeTriggerType triggerType, LWOOBJID origin) const;
|
||||
|
||||
void AddSkills(LWOOBJID origin) const;
|
||||
void RemoveSkills(LWOOBJID origin) const;
|
||||
|
||||
private:
|
||||
std::string name = "";
|
||||
int32_t lot = 0;
|
||||
|
@ -10,6 +10,7 @@ enum class UpgradeTriggerType
|
||||
OnHit,
|
||||
Equip,
|
||||
UnEquip,
|
||||
Active
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -516,7 +516,7 @@ void UserManager::LoginCharacter(const SystemAddress& sysAddr, uint32_t playerID
|
||||
Database::Get()->UpdateLastLoggedInCharacter(playerID);
|
||||
|
||||
uint32_t zoneID = character->GetZoneID();
|
||||
if (zoneID == LWOZONEID_INVALID) zoneID = 1000; //Send char to VE
|
||||
if (zoneID == LWOZONEID_INVALID) zoneID = 1100; //Send char to AG, skip VE
|
||||
|
||||
ZoneInstanceManager::Instance()->RequestZoneTransfer(Game::server, zoneID, character->GetZoneClone(), false, [=](bool mythranShift, uint32_t zoneID, uint32_t zoneInstance, uint32_t zoneClone, std::string serverIP, uint16_t serverPort) {
|
||||
LOG("Transferring %s to Zone %i (Instance %i | Clone %i | Mythran Shift: %s) with IP %s and Port %i", character->GetName().c_str(), zoneID, zoneInstance, zoneClone, mythranShift == true ? "true" : "false", serverIP.c_str(), serverPort);
|
||||
|
@ -1127,15 +1127,31 @@ void InventoryComponent::AddItemSkills(const LOT lot) {
|
||||
|
||||
const auto slot = FindBehaviorSlot(static_cast<eItemType>(info.itemType));
|
||||
|
||||
if (slot == BehaviorSlot::Invalid) {
|
||||
if (slot != BehaviorSlot::Primary) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto skill = FindSkill(lot);
|
||||
|
||||
const auto index = m_Skills.find(slot);
|
||||
|
||||
const auto skill = FindSkill(lot);
|
||||
if (index != m_Skills.end()) {
|
||||
const auto old = index->second;
|
||||
|
||||
SetSkill(slot, skill);
|
||||
if (!old.empty()) {
|
||||
const auto firstElem = *old.begin();
|
||||
|
||||
GameMessages::SendRemoveSkill(m_Parent, firstElem);
|
||||
}
|
||||
}
|
||||
|
||||
m_Skills.erase(slot);
|
||||
|
||||
if (skill != 0) {
|
||||
m_Skills.insert_or_assign(slot, std::set<uint32_t>{ skill });
|
||||
|
||||
GameMessages::SendAddSkill(m_Parent, skill, slot);
|
||||
}
|
||||
}
|
||||
|
||||
void InventoryComponent::RemoveItemSkills(const LOT lot) {
|
||||
@ -1143,7 +1159,7 @@ void InventoryComponent::RemoveItemSkills(const LOT lot) {
|
||||
|
||||
const auto slot = FindBehaviorSlot(static_cast<eItemType>(info.itemType));
|
||||
|
||||
if (slot == BehaviorSlot::Invalid) {
|
||||
if (slot != BehaviorSlot::Primary) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1155,12 +1171,16 @@ void InventoryComponent::RemoveItemSkills(const LOT lot) {
|
||||
|
||||
const auto old = index->second;
|
||||
|
||||
GameMessages::SendRemoveSkill(m_Parent, old);
|
||||
if (!old.empty()) {
|
||||
const auto firstElem = *old.begin();
|
||||
|
||||
GameMessages::SendRemoveSkill(m_Parent, firstElem);
|
||||
}
|
||||
|
||||
m_Skills.erase(slot);
|
||||
|
||||
if (slot == BehaviorSlot::Primary) {
|
||||
m_Skills.insert_or_assign(BehaviorSlot::Primary, 1);
|
||||
m_Skills.insert_or_assign(BehaviorSlot::Primary, std::set<uint32_t>{ 1 });
|
||||
|
||||
GameMessages::SendAddSkill(m_Parent, 1, BehaviorSlot::Primary);
|
||||
}
|
||||
@ -1608,40 +1628,46 @@ bool InventoryComponent::SetSkill(int32_t slot, uint32_t skillId) {
|
||||
bool InventoryComponent::SetSkill(BehaviorSlot slot, uint32_t skillId) {
|
||||
if (skillId == 0) return false;
|
||||
const auto index = m_Skills.find(slot);
|
||||
if (index != m_Skills.end()) {
|
||||
const auto old = index->second;
|
||||
GameMessages::SendRemoveSkill(m_Parent, old);
|
||||
if (index == m_Skills.end()) {
|
||||
m_Skills.insert_or_assign(slot, std::set<uint32_t>{ skillId });
|
||||
} else {
|
||||
auto& existing = index->second;
|
||||
existing.insert(skillId);
|
||||
}
|
||||
|
||||
GameMessages::SendAddSkill(m_Parent, skillId, slot);
|
||||
m_Skills.insert_or_assign(slot, skillId);
|
||||
return true;
|
||||
}
|
||||
|
||||
void InventoryComponent::UnsetSkill(BehaviorSlot slot) {
|
||||
const auto index = m_Skills.find(slot);
|
||||
if (index == m_Skills.end()) return;
|
||||
const auto old = index->second;
|
||||
GameMessages::SendRemoveSkill(m_Parent, old);
|
||||
m_Skills.erase(slot);
|
||||
}
|
||||
|
||||
void InventoryComponent::ResetSkill(BehaviorSlot slot) {
|
||||
const auto index = m_Skills.find(slot);
|
||||
if (index == m_Skills.end()) return;
|
||||
const auto old = index->second;
|
||||
GameMessages::SendRemoveSkill(m_Parent, old);
|
||||
m_Skills.erase(slot);
|
||||
|
||||
// Loop through all equipped items and find the first item that can be equipped in the slot
|
||||
for (const auto& pair : m_Equipped) {
|
||||
const auto item = pair.second;
|
||||
const auto info = Inventory::FindItemComponent(item.lot);
|
||||
const auto behaviorSlot = FindBehaviorSlot(static_cast<eItemType>(info.itemType));
|
||||
|
||||
if (behaviorSlot == slot) {
|
||||
SetSkill(slot, FindSkill(item.lot));
|
||||
return;
|
||||
}
|
||||
void InventoryComponent::UnsetSkill(uint32_t skillId) {
|
||||
for (auto& pair : m_Skills) {
|
||||
auto& skills = pair.second;
|
||||
skills.erase(skillId);
|
||||
}
|
||||
}
|
||||
|
||||
void InventoryComponent::SetSkill(uint32_t skillId) {
|
||||
UnsetSkill(skillId);
|
||||
|
||||
const auto& slotA = m_Skills.find(BehaviorSlot::Head);
|
||||
const auto& slotB = m_Skills.find(BehaviorSlot::Neck);
|
||||
const auto& slotC = m_Skills.find(BehaviorSlot::Offhand);
|
||||
|
||||
// Pick the first one which has less than 3 skills
|
||||
std::set<uint32_t>* slot = nullptr;
|
||||
|
||||
if (slotA == m_Skills.end() || slotA->second.size() < 3) {
|
||||
slot = &m_Skills[BehaviorSlot::Head];
|
||||
} else if (slotB == m_Skills.end() || slotB->second.size() < 3) {
|
||||
slot = &m_Skills[BehaviorSlot::Neck];
|
||||
} else if (slotC == m_Skills.end() || slotC->second.size() < 3) {
|
||||
slot = &m_Skills[BehaviorSlot::Offhand];
|
||||
}
|
||||
|
||||
if (slot == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
slot->insert(skillId);
|
||||
}
|
||||
|
||||
|
||||
|
@ -368,12 +368,12 @@ public:
|
||||
*/
|
||||
void UnequipScripts(Item* unequippedItem);
|
||||
|
||||
std::map<BehaviorSlot, uint32_t> GetSkills(){ return m_Skills; };
|
||||
const std::map<BehaviorSlot, std::set<uint32_t>>& GetSkills(){ return m_Skills; };
|
||||
|
||||
bool SetSkill(int slot, uint32_t skillId);
|
||||
bool SetSkill(int32_t slot, uint32_t skillId);
|
||||
bool SetSkill(BehaviorSlot slot, uint32_t skillId);
|
||||
void UnsetSkill(BehaviorSlot slot);
|
||||
void ResetSkill(BehaviorSlot slot);
|
||||
void UnsetSkill(uint32_t skillId);
|
||||
void SetSkill(uint32_t skillId);
|
||||
|
||||
~InventoryComponent() override;
|
||||
|
||||
@ -389,10 +389,12 @@ private:
|
||||
*/
|
||||
std::map<eInventoryType, Inventory*> m_Inventories;
|
||||
|
||||
std::map<BehaviorSlot, uint32_t> m_ActivatorSkills;
|
||||
|
||||
/**
|
||||
* The skills that this entity currently has active
|
||||
*/
|
||||
std::map<BehaviorSlot, uint32_t> m_Skills;
|
||||
std::map<BehaviorSlot, std::set<uint32_t>> m_Skills;
|
||||
|
||||
/**
|
||||
* The pets this entity has, mapped by object ID and pet info
|
||||
|
@ -31,6 +31,8 @@ ProjectileSyncEntry::ProjectileSyncEntry() {
|
||||
|
||||
std::unordered_map<uint32_t, uint32_t> SkillComponent::m_skillBehaviorCache = {};
|
||||
|
||||
Observable<SkillComponent*, uint32_t, bool> SkillComponent::OnSkillCast;
|
||||
|
||||
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());
|
||||
|
||||
@ -48,6 +50,8 @@ bool SkillComponent::CastPlayerSkill(const uint32_t behaviorId, const uint32_t s
|
||||
|
||||
context->ExecuteUpdates();
|
||||
|
||||
OnSkillCast(this, skillID, !context->failed);
|
||||
|
||||
return !context->failed;
|
||||
}
|
||||
|
||||
|
@ -14,6 +14,7 @@
|
||||
#include "Entity.h"
|
||||
#include "Logger.h"
|
||||
#include "eReplicaComponentType.h"
|
||||
#include "Observable.h"
|
||||
|
||||
struct ProjectileSyncEntry {
|
||||
LWOOBJID id = LWOOBJID_EMPTY;
|
||||
@ -184,6 +185,9 @@ public:
|
||||
*/
|
||||
uint32_t GetUniqueSkillId();
|
||||
|
||||
// SkillComponent, SkillID, Success
|
||||
static Observable<SkillComponent*, uint32_t, bool> OnSkillCast;
|
||||
|
||||
private:
|
||||
/**
|
||||
* All of the active skills mapped by their unique ID.
|
||||
|
Loading…
Reference in New Issue
Block a user