mirror of
https://github.com/DarkflameUniverse/DarkflameServer.git
synced 2024-11-26 23:47:21 +00:00
Merge remote-tracking branch 'origin/main' into dCinema
This commit is contained in:
commit
25e1482a25
@ -31,7 +31,7 @@ void CDZoneTableTable::LoadValuesFromDatabase() {
|
|||||||
entry.smashableMaxDistance = tableData.getFloatField("smashableMaxDistance", -1.0f);
|
entry.smashableMaxDistance = tableData.getFloatField("smashableMaxDistance", -1.0f);
|
||||||
UNUSED(entry.mixerProgram = tableData.getStringField("mixerProgram", ""));
|
UNUSED(entry.mixerProgram = tableData.getStringField("mixerProgram", ""));
|
||||||
UNUSED(entry.clientPhysicsFramerate = tableData.getStringField("clientPhysicsFramerate", ""));
|
UNUSED(entry.clientPhysicsFramerate = tableData.getStringField("clientPhysicsFramerate", ""));
|
||||||
UNUSED(entry.serverPhysicsFramerate = tableData.getStringField("serverPhysicsFramerate", ""));
|
entry.serverPhysicsFramerate = tableData.getStringField("serverPhysicsFramerate", "");
|
||||||
entry.zoneControlTemplate = tableData.getIntField("zoneControlTemplate", -1);
|
entry.zoneControlTemplate = tableData.getIntField("zoneControlTemplate", -1);
|
||||||
entry.widthInChunks = tableData.getIntField("widthInChunks", -1);
|
entry.widthInChunks = tableData.getIntField("widthInChunks", -1);
|
||||||
entry.heightInChunks = tableData.getIntField("heightInChunks", -1);
|
entry.heightInChunks = tableData.getIntField("heightInChunks", -1);
|
||||||
@ -40,10 +40,10 @@ void CDZoneTableTable::LoadValuesFromDatabase() {
|
|||||||
entry.fZoneWeight = tableData.getFloatField("fZoneWeight", -1.0f);
|
entry.fZoneWeight = tableData.getFloatField("fZoneWeight", -1.0f);
|
||||||
UNUSED(entry.thumbnail = tableData.getStringField("thumbnail", ""));
|
UNUSED(entry.thumbnail = tableData.getStringField("thumbnail", ""));
|
||||||
entry.PlayerLoseCoinsOnDeath = tableData.getIntField("PlayerLoseCoinsOnDeath", -1) == 1 ? true : false;
|
entry.PlayerLoseCoinsOnDeath = tableData.getIntField("PlayerLoseCoinsOnDeath", -1) == 1 ? true : false;
|
||||||
UNUSED(entry.disableSaveLoc = tableData.getIntField("disableSaveLoc", -1) == 1 ? true : false);
|
entry.disableSaveLoc = tableData.getIntField("disableSaveLoc", -1) == 1 ? true : false;
|
||||||
entry.teamRadius = tableData.getFloatField("teamRadius", -1.0f);
|
entry.teamRadius = tableData.getFloatField("teamRadius", -1.0f);
|
||||||
UNUSED(entry.gate_version = tableData.getStringField("gate_version", ""));
|
UNUSED(entry.gate_version = tableData.getStringField("gate_version", ""));
|
||||||
UNUSED(entry.mountsAllowed = tableData.getIntField("mountsAllowed", -1) == 1 ? true : false);
|
entry.mountsAllowed = tableData.getIntField("mountsAllowed", -1) == 1 ? true : false;
|
||||||
|
|
||||||
this->m_Entries.insert(std::make_pair(entry.zoneID, entry));
|
this->m_Entries.insert(std::make_pair(entry.zoneID, entry));
|
||||||
tableData.nextRow();
|
tableData.nextRow();
|
||||||
|
@ -18,7 +18,7 @@ struct CDZoneTable {
|
|||||||
float smashableMaxDistance; //!< The maximum smashable distance?
|
float smashableMaxDistance; //!< The maximum smashable distance?
|
||||||
UNUSED(std::string mixerProgram); //!< ???
|
UNUSED(std::string mixerProgram); //!< ???
|
||||||
UNUSED(std::string clientPhysicsFramerate); //!< The client physics framerate
|
UNUSED(std::string clientPhysicsFramerate); //!< The client physics framerate
|
||||||
UNUSED(std::string serverPhysicsFramerate); //!< The server physics framerate
|
std::string serverPhysicsFramerate; //!< The server physics framerate
|
||||||
unsigned int zoneControlTemplate; //!< The Zone Control template
|
unsigned int zoneControlTemplate; //!< The Zone Control template
|
||||||
unsigned int widthInChunks; //!< The width of the world in chunks
|
unsigned int widthInChunks; //!< The width of the world in chunks
|
||||||
unsigned int heightInChunks; //!< The height of the world in chunks
|
unsigned int heightInChunks; //!< The height of the world in chunks
|
||||||
@ -27,10 +27,10 @@ struct CDZoneTable {
|
|||||||
float fZoneWeight; //!< ???
|
float fZoneWeight; //!< ???
|
||||||
UNUSED(std::string thumbnail); //!< The thumbnail of the world
|
UNUSED(std::string thumbnail); //!< The thumbnail of the world
|
||||||
bool PlayerLoseCoinsOnDeath; //!< Whether or not the user loses coins on death
|
bool PlayerLoseCoinsOnDeath; //!< Whether or not the user loses coins on death
|
||||||
UNUSED(bool disableSaveLoc); //!< Disables the saving location?
|
bool disableSaveLoc; //!< Disables the saving location?
|
||||||
float teamRadius; //!< ???
|
float teamRadius; //!< ???
|
||||||
UNUSED(std::string gate_version); //!< The gate version
|
UNUSED(std::string gate_version); //!< The gate version
|
||||||
UNUSED(bool mountsAllowed); //!< Whether or not mounts are allowed
|
bool mountsAllowed; //!< Whether or not mounts are allowed
|
||||||
};
|
};
|
||||||
|
|
||||||
class CDZoneTableTable : public CDTable<CDZoneTableTable> {
|
class CDZoneTableTable : public CDTable<CDZoneTableTable> {
|
||||||
|
@ -314,7 +314,7 @@ void Character::SaveXMLToDatabase() {
|
|||||||
|
|
||||||
auto zoneInfo = Game::zoneManager->GetZone()->GetZoneID();
|
auto zoneInfo = Game::zoneManager->GetZone()->GetZoneID();
|
||||||
// lzid garbage, binary concat of zoneID, zoneInstance and zoneClone
|
// lzid garbage, binary concat of zoneID, zoneInstance and zoneClone
|
||||||
if (zoneInfo.GetMapID() != 0 && zoneInfo.GetCloneID() == 0) {
|
if (zoneInfo.GetMapID() != 0 && zoneInfo.GetCloneID() == 0 && !Game::zoneManager->GetDisableSaveLocation()) {
|
||||||
uint64_t lzidConcat = zoneInfo.GetCloneID();
|
uint64_t lzidConcat = zoneInfo.GetCloneID();
|
||||||
lzidConcat = (lzidConcat << 16) | uint16_t(zoneInfo.GetInstanceID());
|
lzidConcat = (lzidConcat << 16) | uint16_t(zoneInfo.GetInstanceID());
|
||||||
lzidConcat = (lzidConcat << 16) | uint16_t(zoneInfo.GetMapID());
|
lzidConcat = (lzidConcat << 16) | uint16_t(zoneInfo.GetMapID());
|
||||||
|
@ -3,6 +3,8 @@
|
|||||||
#include "Game.h"
|
#include "Game.h"
|
||||||
#include "Logger.h"
|
#include "Logger.h"
|
||||||
#include "EntityManager.h"
|
#include "EntityManager.h"
|
||||||
|
#include "dZoneManager.h"
|
||||||
|
#include "WorldConfig.h"
|
||||||
#include "DestroyableComponent.h"
|
#include "DestroyableComponent.h"
|
||||||
#include "BehaviorContext.h"
|
#include "BehaviorContext.h"
|
||||||
#include "eBasicAttackSuccessTypes.h"
|
#include "eBasicAttackSuccessTypes.h"
|
||||||
@ -13,8 +15,15 @@ void BasicAttackBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bi
|
|||||||
|
|
||||||
auto* destroyableComponent = entity->GetComponent<DestroyableComponent>();
|
auto* destroyableComponent = entity->GetComponent<DestroyableComponent>();
|
||||||
if (destroyableComponent != nullptr) {
|
if (destroyableComponent != nullptr) {
|
||||||
PlayFx(u"onhit", entity->GetObjectID());
|
PlayFx(u"onhit", entity->GetObjectID()); //This damage animation doesn't seem to play consistently
|
||||||
destroyableComponent->Damage(this->m_MaxDamage, context->originator, context->skillID);
|
destroyableComponent->Damage(this->m_MaxDamage, context->originator, context->skillID);
|
||||||
|
|
||||||
|
//Handle player damage cooldown
|
||||||
|
if (entity->IsPlayer() && !this->m_DontApplyImmune) {
|
||||||
|
const float immunityTime = Game::zoneManager->GetWorldConfig()->globalImmunityTime;
|
||||||
|
destroyableComponent->SetDamageCooldownTimer(immunityTime);
|
||||||
|
LOG_DEBUG("Target targetEntity %llu took damage, setting damage cooldown timer to %f s", branch.target, immunityTime);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this->m_OnSuccess->Handle(context, bitStream, branch);
|
this->m_OnSuccess->Handle(context, bitStream, branch);
|
||||||
@ -72,6 +81,7 @@ void BasicAttackBehavior::DoHandleBehavior(BehaviorContext* context, RakNet::Bit
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (isImmune) {
|
if (isImmune) {
|
||||||
|
LOG_DEBUG("Target targetEntity %llu is immune!", branch.target);
|
||||||
this->m_OnFailImmune->Handle(context, bitStream, branch);
|
this->m_OnFailImmune->Handle(context, bitStream, branch);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -178,11 +188,15 @@ void BasicAttackBehavior::DoBehaviorCalculation(BehaviorContext* context, RakNet
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const bool isImmune = destroyableComponent->IsImmune();
|
const float immunityTime = Game::zoneManager->GetWorldConfig()->globalImmunityTime;
|
||||||
|
LOG_DEBUG("Damage cooldown timer currently %f s", destroyableComponent->GetDamageCooldownTimer());
|
||||||
|
|
||||||
|
const bool isImmune = (destroyableComponent->IsImmune()) || (destroyableComponent->IsCooldownImmune());
|
||||||
|
|
||||||
bitStream->Write(isImmune);
|
bitStream->Write(isImmune);
|
||||||
|
|
||||||
if (isImmune) {
|
if (isImmune) {
|
||||||
|
LOG_DEBUG("Target targetEntity %llu is immune!", branch.target);
|
||||||
this->m_OnFailImmune->Calculate(context, bitStream, branch);
|
this->m_OnFailImmune->Calculate(context, bitStream, branch);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -203,6 +217,12 @@ void BasicAttackBehavior::DoBehaviorCalculation(BehaviorContext* context, RakNet
|
|||||||
|
|
||||||
bitStream->Write(isSuccess);
|
bitStream->Write(isSuccess);
|
||||||
|
|
||||||
|
//Handle player damage cooldown
|
||||||
|
if (isSuccess && targetEntity->IsPlayer() && !this->m_DontApplyImmune) {
|
||||||
|
destroyableComponent->SetDamageCooldownTimer(immunityTime);
|
||||||
|
LOG_DEBUG("Target targetEntity %llu took damage, setting damage cooldown timer to %f s", branch.target, immunityTime);
|
||||||
|
}
|
||||||
|
|
||||||
eBasicAttackSuccessTypes successState = eBasicAttackSuccessTypes::FAILIMMUNE;
|
eBasicAttackSuccessTypes successState = eBasicAttackSuccessTypes::FAILIMMUNE;
|
||||||
if (isSuccess) {
|
if (isSuccess) {
|
||||||
if (healthDamageDealt >= 1) {
|
if (healthDamageDealt >= 1) {
|
||||||
@ -236,6 +256,8 @@ void BasicAttackBehavior::DoBehaviorCalculation(BehaviorContext* context, RakNet
|
|||||||
}
|
}
|
||||||
|
|
||||||
void BasicAttackBehavior::Load() {
|
void BasicAttackBehavior::Load() {
|
||||||
|
this->m_DontApplyImmune = GetBoolean("dont_apply_immune");
|
||||||
|
|
||||||
this->m_MinDamage = GetInt("min damage");
|
this->m_MinDamage = GetInt("min damage");
|
||||||
if (this->m_MinDamage == 0) this->m_MinDamage = 1;
|
if (this->m_MinDamage == 0) this->m_MinDamage = 1;
|
||||||
|
|
||||||
|
@ -10,14 +10,14 @@ public:
|
|||||||
/**
|
/**
|
||||||
* @brief Reads a 16bit short from the bitStream and when the actual behavior handling finishes with all of its branches, the bitStream
|
* @brief Reads a 16bit short from the bitStream and when the actual behavior handling finishes with all of its branches, the bitStream
|
||||||
* is then offset to after the allocated bits for this stream.
|
* is then offset to after the allocated bits for this stream.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
void DoHandleBehavior(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch);
|
void DoHandleBehavior(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Handles a client initialized Basic Attack Behavior cast to be deserialized and verified on the server.
|
* @brief Handles a client initialized Basic Attack Behavior cast to be deserialized and verified on the server.
|
||||||
*
|
*
|
||||||
* @param context The Skill's Behavior context. All behaviors in the same tree share the same context
|
* @param context The Skill's Behavior context. All behaviors in the same tree share the same context
|
||||||
* @param bitStream The bitStream to deserialize. BitStreams will always check their bounds before reading in a behavior
|
* @param bitStream The bitStream to deserialize. BitStreams will always check their bounds before reading in a behavior
|
||||||
* and will fail gracefully if an overread is detected.
|
* and will fail gracefully if an overread is detected.
|
||||||
* @param branch The context of this specific branch of the Skill Behavior. Changes based on which branch you are going down.
|
* @param branch The context of this specific branch of the Skill Behavior. Changes based on which branch you are going down.
|
||||||
@ -27,13 +27,13 @@ public:
|
|||||||
/**
|
/**
|
||||||
* @brief Writes a 16bit short to the bitStream and when the actual behavior calculation finishes with all of its branches, the number
|
* @brief Writes a 16bit short to the bitStream and when the actual behavior calculation finishes with all of its branches, the number
|
||||||
* of bits used is then written to where the 16bit short initially was.
|
* of bits used is then written to where the 16bit short initially was.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
void Calculate(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) override;
|
void Calculate(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) override;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Calculates a server initialized Basic Attack Behavior cast to be serialized to the client
|
* @brief Calculates a server initialized Basic Attack Behavior cast to be serialized to the client
|
||||||
*
|
*
|
||||||
* @param context The Skill's Behavior context. All behaviors in the same tree share the same context
|
* @param context The Skill's Behavior context. All behaviors in the same tree share the same context
|
||||||
* @param bitStream The bitStream to serialize to.
|
* @param bitStream The bitStream to serialize to.
|
||||||
* @param branch The context of this specific branch of the Skill Behavior. Changes based on which branch you are going down.
|
* @param branch The context of this specific branch of the Skill Behavior. Changes based on which branch you are going down.
|
||||||
@ -44,10 +44,12 @@ public:
|
|||||||
* @brief Loads this Behaviors parameters from the database. For this behavior specifically:
|
* @brief Loads this Behaviors parameters from the database. For this behavior specifically:
|
||||||
* max and min damage will always be the same. If min is less than max, they are both set to max.
|
* max and min damage will always be the same. If min is less than max, they are both set to max.
|
||||||
* If an action is not in the database, then no action is taken for that result.
|
* If an action is not in the database, then no action is taken for that result.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
void Load() override;
|
void Load() override;
|
||||||
private:
|
private:
|
||||||
|
bool m_DontApplyImmune;
|
||||||
|
|
||||||
uint32_t m_MinDamage;
|
uint32_t m_MinDamage;
|
||||||
|
|
||||||
uint32_t m_MaxDamage;
|
uint32_t m_MaxDamage;
|
||||||
|
@ -187,7 +187,7 @@ void ControllablePhysicsComponent::UpdateXml(tinyxml2::XMLDocument* doc) {
|
|||||||
|
|
||||||
auto zoneInfo = Game::zoneManager->GetZone()->GetZoneID();
|
auto zoneInfo = Game::zoneManager->GetZone()->GetZoneID();
|
||||||
|
|
||||||
if (zoneInfo.GetMapID() != 0 && zoneInfo.GetCloneID() == 0) {
|
if (zoneInfo.GetMapID() != 0 && zoneInfo.GetCloneID() == 0 && !Game::zoneManager->GetDisableSaveLocation()) {
|
||||||
character->SetAttribute("lzx", m_Position.x);
|
character->SetAttribute("lzx", m_Position.x);
|
||||||
character->SetAttribute("lzy", m_Position.y);
|
character->SetAttribute("lzy", m_Position.y);
|
||||||
character->SetAttribute("lzz", m_Position.z);
|
character->SetAttribute("lzz", m_Position.z);
|
||||||
|
@ -73,6 +73,8 @@ DestroyableComponent::DestroyableComponent(Entity* parent) : Component(parent) {
|
|||||||
m_ImmuneToQuickbuildInterruptCount = 0;
|
m_ImmuneToQuickbuildInterruptCount = 0;
|
||||||
m_ImmuneToPullToPointCount = 0;
|
m_ImmuneToPullToPointCount = 0;
|
||||||
m_DeathBehavior = -1;
|
m_DeathBehavior = -1;
|
||||||
|
|
||||||
|
m_DamageCooldownTimer = 0.0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
DestroyableComponent::~DestroyableComponent() {
|
DestroyableComponent::~DestroyableComponent() {
|
||||||
@ -179,6 +181,10 @@ void DestroyableComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsIn
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void DestroyableComponent::Update(float deltaTime) {
|
||||||
|
m_DamageCooldownTimer -= deltaTime;
|
||||||
|
}
|
||||||
|
|
||||||
void DestroyableComponent::LoadFromXml(tinyxml2::XMLDocument* doc) {
|
void DestroyableComponent::LoadFromXml(tinyxml2::XMLDocument* doc) {
|
||||||
tinyxml2::XMLElement* dest = doc->FirstChildElement("obj")->FirstChildElement("dest");
|
tinyxml2::XMLElement* dest = doc->FirstChildElement("obj")->FirstChildElement("dest");
|
||||||
if (!dest) {
|
if (!dest) {
|
||||||
@ -409,7 +415,7 @@ void DestroyableComponent::AddFaction(const int32_t factionID, const bool ignore
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool DestroyableComponent::IsEnemy(const Entity* other) const {
|
bool DestroyableComponent::IsEnemy(const Entity* other) const {
|
||||||
if (m_Parent->IsPlayer() && other->IsPlayer()){
|
if (m_Parent->IsPlayer() && other->IsPlayer()) {
|
||||||
auto* thisCharacterComponent = m_Parent->GetComponent<CharacterComponent>();
|
auto* thisCharacterComponent = m_Parent->GetComponent<CharacterComponent>();
|
||||||
if (!thisCharacterComponent) return false;
|
if (!thisCharacterComponent) return false;
|
||||||
auto* otherCharacterComponent = other->GetComponent<CharacterComponent>();
|
auto* otherCharacterComponent = other->GetComponent<CharacterComponent>();
|
||||||
@ -464,6 +470,10 @@ bool DestroyableComponent::IsImmune() const {
|
|||||||
return m_IsGMImmune || m_ImmuneToBasicAttackCount > 0;
|
return m_IsGMImmune || m_ImmuneToBasicAttackCount > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool DestroyableComponent::IsCooldownImmune() const {
|
||||||
|
return m_DamageCooldownTimer > 0.0f;
|
||||||
|
}
|
||||||
|
|
||||||
bool DestroyableComponent::IsKnockbackImmune() const {
|
bool DestroyableComponent::IsKnockbackImmune() const {
|
||||||
auto* characterComponent = m_Parent->GetComponent<CharacterComponent>();
|
auto* characterComponent = m_Parent->GetComponent<CharacterComponent>();
|
||||||
auto* inventoryComponent = m_Parent->GetComponent<InventoryComponent>();
|
auto* inventoryComponent = m_Parent->GetComponent<InventoryComponent>();
|
||||||
@ -546,7 +556,8 @@ void DestroyableComponent::Damage(uint32_t damage, const LWOOBJID source, uint32
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (IsImmune()) {
|
if (IsImmune() || IsCooldownImmune()) {
|
||||||
|
LOG_DEBUG("Target targetEntity %llu is immune!", m_Parent->GetObjectID()); //Immune is succesfully proc'd
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -634,9 +645,9 @@ void DestroyableComponent::Damage(uint32_t damage, const LWOOBJID source, uint32
|
|||||||
}
|
}
|
||||||
|
|
||||||
//check if hardcore mode is enabled
|
//check if hardcore mode is enabled
|
||||||
if (Game::entityManager->GetHardcoreMode()) {
|
if (Game::entityManager->GetHardcoreMode()) {
|
||||||
DoHardcoreModeDrops(source);
|
DoHardcoreModeDrops(source);
|
||||||
}
|
}
|
||||||
|
|
||||||
Smash(source, eKillType::VIOLENT, u"", skillID);
|
Smash(source, eKillType::VIOLENT, u"", skillID);
|
||||||
}
|
}
|
||||||
@ -796,16 +807,16 @@ void DestroyableComponent::SetFaction(int32_t factionID, bool ignoreChecks) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void DestroyableComponent::SetStatusImmunity(
|
void DestroyableComponent::SetStatusImmunity(
|
||||||
const eStateChangeType state,
|
const eStateChangeType state,
|
||||||
const bool bImmuneToBasicAttack,
|
const bool bImmuneToBasicAttack,
|
||||||
const bool bImmuneToDamageOverTime,
|
const bool bImmuneToDamageOverTime,
|
||||||
const bool bImmuneToKnockback,
|
const bool bImmuneToKnockback,
|
||||||
const bool bImmuneToInterrupt,
|
const bool bImmuneToInterrupt,
|
||||||
const bool bImmuneToSpeed,
|
const bool bImmuneToSpeed,
|
||||||
const bool bImmuneToImaginationGain,
|
const bool bImmuneToImaginationGain,
|
||||||
const bool bImmuneToImaginationLoss,
|
const bool bImmuneToImaginationLoss,
|
||||||
const bool bImmuneToQuickbuildInterrupt,
|
const bool bImmuneToQuickbuildInterrupt,
|
||||||
const bool bImmuneToPullToPoint) {
|
const bool bImmuneToPullToPoint) {
|
||||||
|
|
||||||
if (state == eStateChangeType::POP) {
|
if (state == eStateChangeType::POP) {
|
||||||
if (bImmuneToBasicAttack && m_ImmuneToBasicAttackCount > 0) m_ImmuneToBasicAttackCount -= 1;
|
if (bImmuneToBasicAttack && m_ImmuneToBasicAttackCount > 0) m_ImmuneToBasicAttackCount -= 1;
|
||||||
@ -818,7 +829,7 @@ void DestroyableComponent::SetStatusImmunity(
|
|||||||
if (bImmuneToQuickbuildInterrupt && m_ImmuneToQuickbuildInterruptCount > 0) m_ImmuneToQuickbuildInterruptCount -= 1;
|
if (bImmuneToQuickbuildInterrupt && m_ImmuneToQuickbuildInterruptCount > 0) m_ImmuneToQuickbuildInterruptCount -= 1;
|
||||||
if (bImmuneToPullToPoint && m_ImmuneToPullToPointCount > 0) m_ImmuneToPullToPointCount -= 1;
|
if (bImmuneToPullToPoint && m_ImmuneToPullToPointCount > 0) m_ImmuneToPullToPointCount -= 1;
|
||||||
|
|
||||||
} else if (state == eStateChangeType::PUSH){
|
} else if (state == eStateChangeType::PUSH) {
|
||||||
if (bImmuneToBasicAttack) m_ImmuneToBasicAttackCount += 1;
|
if (bImmuneToBasicAttack) m_ImmuneToBasicAttackCount += 1;
|
||||||
if (bImmuneToDamageOverTime) m_ImmuneToDamageOverTimeCount += 1;
|
if (bImmuneToDamageOverTime) m_ImmuneToDamageOverTimeCount += 1;
|
||||||
if (bImmuneToKnockback) m_ImmuneToKnockbackCount += 1;
|
if (bImmuneToKnockback) m_ImmuneToKnockbackCount += 1;
|
||||||
@ -945,7 +956,7 @@ void DestroyableComponent::AddOnHitCallback(const std::function<void(Entity*)>&
|
|||||||
m_OnHitCallbacks.push_back(callback);
|
m_OnHitCallbacks.push_back(callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
void DestroyableComponent::DoHardcoreModeDrops(const LWOOBJID source){
|
void DestroyableComponent::DoHardcoreModeDrops(const LWOOBJID source) {
|
||||||
//check if this is a player:
|
//check if this is a player:
|
||||||
if (m_Parent->IsPlayer()) {
|
if (m_Parent->IsPlayer()) {
|
||||||
//remove hardcore_lose_uscore_on_death_percent from the player's uscore:
|
//remove hardcore_lose_uscore_on_death_percent from the player's uscore:
|
||||||
@ -963,9 +974,9 @@ void DestroyableComponent::DoHardcoreModeDrops(const LWOOBJID source){
|
|||||||
if (inventory) {
|
if (inventory) {
|
||||||
//get the items inventory:
|
//get the items inventory:
|
||||||
auto items = inventory->GetInventory(eInventoryType::ITEMS);
|
auto items = inventory->GetInventory(eInventoryType::ITEMS);
|
||||||
if (items){
|
if (items) {
|
||||||
auto itemMap = items->GetItems();
|
auto itemMap = items->GetItems();
|
||||||
if (!itemMap.empty()){
|
if (!itemMap.empty()) {
|
||||||
for (const auto& item : itemMap) {
|
for (const auto& item : itemMap) {
|
||||||
//drop the item:
|
//drop the item:
|
||||||
if (!item.second) continue;
|
if (!item.second) continue;
|
||||||
|
@ -24,6 +24,7 @@ public:
|
|||||||
DestroyableComponent(Entity* parentEntity);
|
DestroyableComponent(Entity* parentEntity);
|
||||||
~DestroyableComponent() override;
|
~DestroyableComponent() override;
|
||||||
|
|
||||||
|
void Update(float deltaTime) override;
|
||||||
void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate) override;
|
void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate) override;
|
||||||
void LoadFromXml(tinyxml2::XMLDocument* doc) override;
|
void LoadFromXml(tinyxml2::XMLDocument* doc) override;
|
||||||
void UpdateXml(tinyxml2::XMLDocument* doc) override;
|
void UpdateXml(tinyxml2::XMLDocument* doc) override;
|
||||||
@ -166,6 +167,11 @@ public:
|
|||||||
*/
|
*/
|
||||||
bool IsImmune() const;
|
bool IsImmune() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return whether this entity is currently immune to attacks due to a damage cooldown period
|
||||||
|
*/
|
||||||
|
bool IsCooldownImmune() const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets if this entity has GM immunity, making it not killable
|
* Sets if this entity has GM immunity, making it not killable
|
||||||
* @param value the GM immunity of this entity
|
* @param value the GM immunity of this entity
|
||||||
@ -406,18 +412,23 @@ public:
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Getters for status immunities
|
// Getters for status immunities
|
||||||
const bool GetImmuneToBasicAttack() {return m_ImmuneToBasicAttackCount > 0;};
|
const bool GetImmuneToBasicAttack() { return m_ImmuneToBasicAttackCount > 0; };
|
||||||
const bool GetImmuneToDamageOverTime() {return m_ImmuneToDamageOverTimeCount > 0;};
|
const bool GetImmuneToDamageOverTime() { return m_ImmuneToDamageOverTimeCount > 0; };
|
||||||
const bool GetImmuneToKnockback() {return m_ImmuneToKnockbackCount > 0;};
|
const bool GetImmuneToKnockback() { return m_ImmuneToKnockbackCount > 0; };
|
||||||
const bool GetImmuneToInterrupt() {return m_ImmuneToInterruptCount > 0;};
|
const bool GetImmuneToInterrupt() { return m_ImmuneToInterruptCount > 0; };
|
||||||
const bool GetImmuneToSpeed() {return m_ImmuneToSpeedCount > 0;};
|
const bool GetImmuneToSpeed() { return m_ImmuneToSpeedCount > 0; };
|
||||||
const bool GetImmuneToImaginationGain() {return m_ImmuneToImaginationGainCount > 0;};
|
const bool GetImmuneToImaginationGain() { return m_ImmuneToImaginationGainCount > 0; };
|
||||||
const bool GetImmuneToImaginationLoss() {return m_ImmuneToImaginationLossCount > 0;};
|
const bool GetImmuneToImaginationLoss() { return m_ImmuneToImaginationLossCount > 0; };
|
||||||
const bool GetImmuneToQuickbuildInterrupt() {return m_ImmuneToQuickbuildInterruptCount > 0;};
|
const bool GetImmuneToQuickbuildInterrupt() { return m_ImmuneToQuickbuildInterruptCount > 0; };
|
||||||
const bool GetImmuneToPullToPoint() {return m_ImmuneToPullToPointCount > 0;};
|
const bool GetImmuneToPullToPoint() { return m_ImmuneToPullToPointCount > 0; };
|
||||||
|
|
||||||
int32_t GetDeathBehavior() const { return m_DeathBehavior; }
|
// Damage cooldown setters/getters
|
||||||
|
void SetDamageCooldownTimer(float value) { m_DamageCooldownTimer = value; }
|
||||||
|
float GetDamageCooldownTimer() { return m_DamageCooldownTimer; }
|
||||||
|
|
||||||
|
// Death behavior setters/getters
|
||||||
void SetDeathBehavior(int32_t value) { m_DeathBehavior = value; }
|
void SetDeathBehavior(int32_t value) { m_DeathBehavior = value; }
|
||||||
|
int32_t GetDeathBehavior() const { return m_DeathBehavior; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Utility to reset all stats to the default stats based on items and completed missions
|
* Utility to reset all stats to the default stats based on items and completed missions
|
||||||
@ -605,6 +616,11 @@ private:
|
|||||||
* Death behavior type. If 0, the client plays a death animation as opposed to a smash animation.
|
* Death behavior type. If 0, the client plays a death animation as opposed to a smash animation.
|
||||||
*/
|
*/
|
||||||
int32_t m_DeathBehavior;
|
int32_t m_DeathBehavior;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Damage immunity cooldown timer. Set to a value that then counts down to create a damage cooldown for players
|
||||||
|
*/
|
||||||
|
float m_DamageCooldownTimer;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // DESTROYABLECOMPONENT_H
|
#endif // DESTROYABLECOMPONENT_H
|
||||||
|
@ -29,6 +29,7 @@
|
|||||||
#include "RenderComponent.h"
|
#include "RenderComponent.h"
|
||||||
#include "eObjectBits.h"
|
#include "eObjectBits.h"
|
||||||
#include "eGameMasterLevel.h"
|
#include "eGameMasterLevel.h"
|
||||||
|
#include "eMissionState.h"
|
||||||
|
|
||||||
std::unordered_map<LOT, PetComponent::PetPuzzleData> PetComponent::buildCache{};
|
std::unordered_map<LOT, PetComponent::PetPuzzleData> PetComponent::buildCache{};
|
||||||
std::unordered_map<LWOOBJID, LWOOBJID> PetComponent::currentActivities{};
|
std::unordered_map<LWOOBJID, LWOOBJID> PetComponent::currentActivities{};
|
||||||
@ -439,9 +440,15 @@ void PetComponent::Update(float deltaTime) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto* missionComponent = owner->GetComponent<MissionComponent>();
|
||||||
|
if (!missionComponent) return;
|
||||||
|
|
||||||
|
// Determine if the "Lost Tags" mission has been completed and digging has been unlocked
|
||||||
|
const bool digUnlocked = missionComponent->GetMissionState(842) == eMissionState::COMPLETE;
|
||||||
|
|
||||||
Entity* closestTresure = PetDigServer::GetClosestTresure(position);
|
Entity* closestTresure = PetDigServer::GetClosestTresure(position);
|
||||||
|
|
||||||
if (closestTresure != nullptr) {
|
if (closestTresure != nullptr && digUnlocked) {
|
||||||
// Skeleton Dragon Pat special case for bone digging
|
// Skeleton Dragon Pat special case for bone digging
|
||||||
if (closestTresure->GetLOT() == 12192 && m_Parent->GetLOT() != 13067) {
|
if (closestTresure->GetLOT() == 12192 && m_Parent->GetLOT() != 13067) {
|
||||||
goto skipTresure;
|
goto skipTresure;
|
||||||
|
@ -19,6 +19,8 @@
|
|||||||
#include "eObjectBits.h"
|
#include "eObjectBits.h"
|
||||||
#include "eReplicaComponentType.h"
|
#include "eReplicaComponentType.h"
|
||||||
#include "eUseItemResponse.h"
|
#include "eUseItemResponse.h"
|
||||||
|
#include "dZoneManager.h"
|
||||||
|
#include "ChatPackets.h"
|
||||||
|
|
||||||
#include "CDBrickIDTableTable.h"
|
#include "CDBrickIDTableTable.h"
|
||||||
#include "CDObjectSkillsTable.h"
|
#include "CDObjectSkillsTable.h"
|
||||||
@ -292,12 +294,19 @@ void Item::UseNonEquip(Item* item) {
|
|||||||
|
|
||||||
const auto type = static_cast<eItemType>(info->itemType);
|
const auto type = static_cast<eItemType>(info->itemType);
|
||||||
if (type == eItemType::MOUNT) {
|
if (type == eItemType::MOUNT) {
|
||||||
playerInventoryComponent->HandlePossession(this);
|
if (Game::zoneManager->GetMountsAllowed()){
|
||||||
// TODO Check if mounts are allowed to be spawned
|
playerInventoryComponent->HandlePossession(this);
|
||||||
} else if (type == eItemType::PET_INVENTORY_ITEM && subKey != LWOOBJID_EMPTY) {
|
} else {
|
||||||
const auto& databasePet = playerInventoryComponent->GetDatabasePet(subKey);
|
ChatPackets::SendSystemMessage(playerEntity->GetSystemAddress(), u"Mounts are not allowed in this zone");
|
||||||
if (databasePet.lot != LOT_NULL) {
|
}
|
||||||
playerInventoryComponent->SpawnPet(this);
|
} else if (type == eItemType::PET_INVENTORY_ITEM && subKey != LWOOBJID_EMPTY ) {
|
||||||
|
if (Game::zoneManager->GetPetsAllowed()){
|
||||||
|
const auto& databasePet = playerInventoryComponent->GetDatabasePet(subKey);
|
||||||
|
if (databasePet.lot != LOT_NULL) {
|
||||||
|
playerInventoryComponent->SpawnPet(this);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ChatPackets::SendSystemMessage(playerEntity->GetSystemAddress(), u"Pets are not allowed in this zone");
|
||||||
}
|
}
|
||||||
// This precondition response is taken care of in SpawnPet().
|
// This precondition response is taken care of in SpawnPet().
|
||||||
} else {
|
} else {
|
||||||
@ -433,7 +442,7 @@ void Item::DisassembleModel() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto* doc = new tinyxml2::XMLDocument();
|
std::unique_ptr<tinyxml2::XMLDocument> doc(new tinyxml2::XMLDocument());
|
||||||
|
|
||||||
if (!doc) {
|
if (!doc) {
|
||||||
return;
|
return;
|
||||||
|
@ -252,7 +252,7 @@ void SlashCommandHandler::HandleChatCommand(const std::u16string& command, Entit
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (chatCommand == "credits" || chatCommand == "info") {
|
if (chatCommand == "credits" || chatCommand == "info") {
|
||||||
const auto& customText = chatCommand == "credits" ? VanityUtilities::ParseMarkdown((BinaryPathFinder::GetBinaryDir() / "vanity/CREDITS.md").string()) : VanityUtilities::ParseMarkdown((BinaryPathFinder::GetBinaryDir() / "vanity/INFO.md").string());
|
const auto& customText = chatCommand == "credits" ? VanityUtilities::ParseMarkdown((BinaryPathFinder::GetBinaryDir() / "vanity/CREDITS.md").string()) : VanityUtilities::ParseMarkdown((BinaryPathFinder::GetBinaryDir() / "vanity/INFO.md").string());
|
||||||
|
|
||||||
{
|
{
|
||||||
AMFArrayValue args;
|
AMFArrayValue args;
|
||||||
@ -1666,6 +1666,24 @@ void SlashCommandHandler::HandleChatCommand(const std::u16string& command, Entit
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Testing basic attack immunity
|
||||||
|
if (chatCommand == "attackimmune" && args.size() >= 1 && entity->GetGMLevel() >= eGameMasterLevel::DEVELOPER) {
|
||||||
|
auto* destroyableComponent = entity->GetComponent<DestroyableComponent>();
|
||||||
|
|
||||||
|
int32_t state = false;
|
||||||
|
|
||||||
|
if (!GeneralUtils::TryParse(args[0], state)) {
|
||||||
|
ChatPackets::SendSystemMessage(sysAddr, u"Invalid state.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (destroyableComponent != nullptr) {
|
||||||
|
destroyableComponent->SetIsImmune(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (chatCommand == "buff" && args.size() >= 2 && entity->GetGMLevel() >= eGameMasterLevel::DEVELOPER) {
|
if (chatCommand == "buff" && args.size() >= 2 && entity->GetGMLevel() >= eGameMasterLevel::DEVELOPER) {
|
||||||
auto* buffComponent = entity->GetComponent<BuffComponent>();
|
auto* buffComponent = entity->GetComponent<BuffComponent>();
|
||||||
|
|
||||||
@ -2019,7 +2037,7 @@ void SlashCommandHandler::HandleChatCommand(const std::u16string& command, Entit
|
|||||||
|
|
||||||
if (chatCommand == "castskill" && entity->GetGMLevel() >= eGameMasterLevel::DEVELOPER && args.size() >= 1) {
|
if (chatCommand == "castskill" && entity->GetGMLevel() >= eGameMasterLevel::DEVELOPER && args.size() >= 1) {
|
||||||
auto* skillComponent = entity->GetComponent<SkillComponent>();
|
auto* skillComponent = entity->GetComponent<SkillComponent>();
|
||||||
if (skillComponent){
|
if (skillComponent) {
|
||||||
uint32_t skillId;
|
uint32_t skillId;
|
||||||
|
|
||||||
if (!GeneralUtils::TryParse(args[0], skillId)) {
|
if (!GeneralUtils::TryParse(args[0], skillId)) {
|
||||||
@ -2036,7 +2054,7 @@ void SlashCommandHandler::HandleChatCommand(const std::u16string& command, Entit
|
|||||||
uint32_t skillId;
|
uint32_t skillId;
|
||||||
int slot;
|
int slot;
|
||||||
auto* inventoryComponent = entity->GetComponent<InventoryComponent>();
|
auto* inventoryComponent = entity->GetComponent<InventoryComponent>();
|
||||||
if (inventoryComponent){
|
if (inventoryComponent) {
|
||||||
if (!GeneralUtils::TryParse(args[0], slot)) {
|
if (!GeneralUtils::TryParse(args[0], slot)) {
|
||||||
ChatPackets::SendSystemMessage(sysAddr, u"Error getting slot.");
|
ChatPackets::SendSystemMessage(sysAddr, u"Error getting slot.");
|
||||||
return;
|
return;
|
||||||
@ -2045,7 +2063,7 @@ void SlashCommandHandler::HandleChatCommand(const std::u16string& command, Entit
|
|||||||
ChatPackets::SendSystemMessage(sysAddr, u"Error getting skill.");
|
ChatPackets::SendSystemMessage(sysAddr, u"Error getting skill.");
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
if(inventoryComponent->SetSkill(slot, skillId)) ChatPackets::SendSystemMessage(sysAddr, u"Set skill to slot successfully");
|
if (inventoryComponent->SetSkill(slot, skillId)) ChatPackets::SendSystemMessage(sysAddr, u"Set skill to slot successfully");
|
||||||
else ChatPackets::SendSystemMessage(sysAddr, u"Set skill to slot failed");
|
else ChatPackets::SendSystemMessage(sysAddr, u"Set skill to slot failed");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2054,7 +2072,7 @@ void SlashCommandHandler::HandleChatCommand(const std::u16string& command, Entit
|
|||||||
|
|
||||||
if (chatCommand == "setfaction" && entity->GetGMLevel() >= eGameMasterLevel::DEVELOPER && args.size() >= 1) {
|
if (chatCommand == "setfaction" && entity->GetGMLevel() >= eGameMasterLevel::DEVELOPER && args.size() >= 1) {
|
||||||
auto* destroyableComponent = entity->GetComponent<DestroyableComponent>();
|
auto* destroyableComponent = entity->GetComponent<DestroyableComponent>();
|
||||||
if (destroyableComponent){
|
if (destroyableComponent) {
|
||||||
int32_t faction;
|
int32_t faction;
|
||||||
|
|
||||||
if (!GeneralUtils::TryParse(args[0], faction)) {
|
if (!GeneralUtils::TryParse(args[0], faction)) {
|
||||||
@ -2069,7 +2087,7 @@ void SlashCommandHandler::HandleChatCommand(const std::u16string& command, Entit
|
|||||||
|
|
||||||
if (chatCommand == "addfaction" && entity->GetGMLevel() >= eGameMasterLevel::DEVELOPER && args.size() >= 1) {
|
if (chatCommand == "addfaction" && entity->GetGMLevel() >= eGameMasterLevel::DEVELOPER && args.size() >= 1) {
|
||||||
auto* destroyableComponent = entity->GetComponent<DestroyableComponent>();
|
auto* destroyableComponent = entity->GetComponent<DestroyableComponent>();
|
||||||
if (destroyableComponent){
|
if (destroyableComponent) {
|
||||||
int32_t faction;
|
int32_t faction;
|
||||||
|
|
||||||
if (!GeneralUtils::TryParse(args[0], faction)) {
|
if (!GeneralUtils::TryParse(args[0], faction)) {
|
||||||
@ -2084,7 +2102,7 @@ void SlashCommandHandler::HandleChatCommand(const std::u16string& command, Entit
|
|||||||
|
|
||||||
if (chatCommand == "getfactions" && entity->GetGMLevel() >= eGameMasterLevel::DEVELOPER) {
|
if (chatCommand == "getfactions" && entity->GetGMLevel() >= eGameMasterLevel::DEVELOPER) {
|
||||||
auto* destroyableComponent = entity->GetComponent<DestroyableComponent>();
|
auto* destroyableComponent = entity->GetComponent<DestroyableComponent>();
|
||||||
if (destroyableComponent){
|
if (destroyableComponent) {
|
||||||
ChatPackets::SendSystemMessage(sysAddr, u"Friendly factions:");
|
ChatPackets::SendSystemMessage(sysAddr, u"Friendly factions:");
|
||||||
for (const auto entry : destroyableComponent->GetFactionIDs()) {
|
for (const auto entry : destroyableComponent->GetFactionIDs()) {
|
||||||
ChatPackets::SendSystemMessage(sysAddr, (GeneralUtils::to_u16string(entry)));
|
ChatPackets::SendSystemMessage(sysAddr, (GeneralUtils::to_u16string(entry)));
|
||||||
|
@ -15,6 +15,10 @@
|
|||||||
#include "Logger.h"
|
#include "Logger.h"
|
||||||
#include "BinaryPathFinder.h"
|
#include "BinaryPathFinder.h"
|
||||||
#include "EntityInfo.h"
|
#include "EntityInfo.h"
|
||||||
|
#include "Spawner.h"
|
||||||
|
#include "dZoneManager.h"
|
||||||
|
#include "../dWorldServer/ObjectIDManager.h"
|
||||||
|
#include "Level.h"
|
||||||
|
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
|
|
||||||
@ -29,6 +33,21 @@ void VanityUtilities::SpawnVanity() {
|
|||||||
|
|
||||||
const uint32_t zoneID = Game::server->GetZoneID();
|
const uint32_t zoneID = Game::server->GetZoneID();
|
||||||
|
|
||||||
|
for (const auto& npc : m_NPCs) {
|
||||||
|
if (npc.m_ID == LWOOBJID_EMPTY) continue;
|
||||||
|
if (npc.m_LOT == 176){
|
||||||
|
Game::zoneManager->RemoveSpawner(npc.m_ID);
|
||||||
|
} else{
|
||||||
|
auto* entity = Game::entityManager->GetEntity(npc.m_ID);
|
||||||
|
if (!entity) continue;
|
||||||
|
entity->Smash(LWOOBJID_EMPTY, eKillType::VIOLENT);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
m_NPCs.clear();
|
||||||
|
m_Parties.clear();
|
||||||
|
m_PartyPhrases.clear();
|
||||||
|
|
||||||
ParseXML((BinaryPathFinder::GetBinaryDir() / "vanity/NPC.xml").string());
|
ParseXML((BinaryPathFinder::GetBinaryDir() / "vanity/NPC.xml").string());
|
||||||
|
|
||||||
// Loop through all parties
|
// Loop through all parties
|
||||||
@ -53,7 +72,7 @@ void VanityUtilities::SpawnVanity() {
|
|||||||
|
|
||||||
// Loop through all locations
|
// Loop through all locations
|
||||||
for (const auto& location : party.m_Locations) {
|
for (const auto& location : party.m_Locations) {
|
||||||
rate = GeneralUtils::GenerateRandomNumber<float>(0, 1);
|
rate = GeneralUtils::GenerateRandomNumber<float>(0, 1);
|
||||||
if (0.75f < rate) {
|
if (0.75f < rate) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -66,10 +85,11 @@ void VanityUtilities::SpawnVanity() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
auto& npc = npcList[npcIndex];
|
auto& npc = npcList[npcIndex];
|
||||||
|
// Skip spawners
|
||||||
|
if (npc.m_LOT == 176) continue;
|
||||||
|
|
||||||
taken.push_back(npcIndex);
|
taken.push_back(npcIndex);
|
||||||
|
|
||||||
// Spawn the NPC
|
|
||||||
LOG("ldf size is %i", npc.ldf.size());
|
LOG("ldf size is %i", npc.ldf.size());
|
||||||
if (npc.ldf.empty()) {
|
if (npc.ldf.empty()) {
|
||||||
npc.ldf = {
|
npc.ldf = {
|
||||||
@ -79,13 +99,16 @@ void VanityUtilities::SpawnVanity() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Spawn the NPC
|
// Spawn the NPC
|
||||||
auto* npcEntity = SpawnNPC(npc.m_LOT, npc.m_Name, location.m_Position, location.m_Rotation, npc.m_Equipment, npc.ldf);
|
if (npc.m_LOT == 176){
|
||||||
if (!npc.m_Phrases.empty()) {
|
npc.m_ID = SpawnSpawner(npc.m_LOT, location.m_Position, location.m_Rotation, npc.ldf);
|
||||||
npcEntity->SetVar<std::vector<std::string>>(u"chats", m_PartyPhrases);
|
} else {
|
||||||
SetupNPCTalk(npcEntity);
|
auto* npcEntity = SpawnNPC(npc.m_LOT, npc.m_Name, location.m_Position, location.m_Rotation, npc.m_Equipment, npc.ldf);
|
||||||
|
if (!npc.m_Phrases.empty()) {
|
||||||
|
npcEntity->SetVar<std::vector<std::string>>(u"chats", m_PartyPhrases);
|
||||||
|
SetupNPCTalk(npcEntity);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -111,27 +134,27 @@ void VanityUtilities::SpawnVanity() {
|
|||||||
new LDFData<std::u16string>(u"custom_script_client", u"scripts\\ai\\SPEC\\MISSION_MINIGAME_CLIENT.lua")
|
new LDFData<std::u16string>(u"custom_script_client", u"scripts\\ai\\SPEC\\MISSION_MINIGAME_CLIENT.lua")
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
if (npc.m_LOT == 176){
|
||||||
|
npc.m_ID = SpawnSpawner(npc.m_LOT, location.m_Position, location.m_Rotation, npc.ldf);
|
||||||
|
} else {
|
||||||
|
// Spawn the NPC
|
||||||
|
auto* npcEntity = SpawnNPC(npc.m_LOT, npc.m_Name, location.m_Position, location.m_Rotation, npc.m_Equipment, npc.ldf);
|
||||||
|
if (!npcEntity) continue;
|
||||||
|
npc.m_ID = npcEntity->GetObjectID();
|
||||||
|
if (!npc.m_Phrases.empty()){
|
||||||
|
npcEntity->SetVar<std::vector<std::string>>(u"chats", npc.m_Phrases);
|
||||||
|
|
||||||
// Spawn the NPC
|
auto* scriptComponent = npcEntity->GetComponent<ScriptComponent>();
|
||||||
auto* npcEntity = SpawnNPC(npc.m_LOT, npc.m_Name, location.m_Position, location.m_Rotation, npc.m_Equipment, npc.ldf);
|
|
||||||
if (!npc.m_Phrases.empty()){
|
|
||||||
npcEntity->SetVar<std::vector<std::string>>(u"chats", npc.m_Phrases);
|
|
||||||
|
|
||||||
SetupNPCTalk(npcEntity);
|
if (scriptComponent && !npc.m_Script.empty()) {
|
||||||
}
|
scriptComponent->SetScript(npc.m_Script);
|
||||||
|
scriptComponent->SetSerialized(false);
|
||||||
|
|
||||||
auto* scriptComponent = npcEntity->GetComponent<ScriptComponent>();
|
for (const auto& npc : npc.m_Flags) {
|
||||||
|
npcEntity->SetVar<bool>(GeneralUtils::ASCIIToUTF16(npc.first), npc.second);
|
||||||
LOG_DEBUG("Script: %s", npc.m_Script.c_str());
|
}
|
||||||
|
}
|
||||||
if (scriptComponent && !npc.m_Script.empty()) {
|
SetupNPCTalk(npcEntity);
|
||||||
scriptComponent->SetScript(npc.m_Script);
|
|
||||||
scriptComponent->SetSerialized(false);
|
|
||||||
|
|
||||||
LOG_DEBUG("Setting script to %s", npc.m_Script.c_str());
|
|
||||||
|
|
||||||
for (const auto& npc : npc.m_Flags) {
|
|
||||||
npcEntity->SetVar<bool>(GeneralUtils::ASCIIToUTF16(npc.first), npc.second);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -154,8 +177,21 @@ void VanityUtilities::SpawnVanity() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Entity* VanityUtilities::SpawnNPC(LOT lot, const std::string& name, const NiPoint3& position,
|
LWOOBJID VanityUtilities::SpawnSpawner(LOT lot, const NiPoint3& position, const NiQuaternion& rotation, const std::vector<LDFBaseData*>& ldf){
|
||||||
const NiQuaternion& rotation, const std::vector<LOT>& inventory, const std::vector<LDFBaseData*>& ldf) {
|
SceneObject obj;
|
||||||
|
obj.lot = lot;
|
||||||
|
// guratantee we have no collisions
|
||||||
|
do {
|
||||||
|
obj.id = ObjectIDManager::Instance()->GenerateObjectID();
|
||||||
|
} while(Game::zoneManager->GetSpawner(obj.id));
|
||||||
|
obj.position = position;
|
||||||
|
obj.rotation = rotation;
|
||||||
|
obj.settings = ldf;
|
||||||
|
Level::MakeSpawner(obj);
|
||||||
|
return obj.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
Entity* VanityUtilities::SpawnNPC(LOT lot, const std::string& name, const NiPoint3& position, const NiQuaternion& rotation, const std::vector<LOT>& inventory, const std::vector<LDFBaseData*>& ldf) {
|
||||||
EntityInfo info;
|
EntityInfo info;
|
||||||
info.lot = lot;
|
info.lot = lot;
|
||||||
info.pos = position;
|
info.pos = position;
|
||||||
@ -167,9 +203,6 @@ Entity* VanityUtilities::SpawnNPC(LOT lot, const std::string& name, const NiPoin
|
|||||||
entity->SetVar(u"npcName", name);
|
entity->SetVar(u"npcName", name);
|
||||||
if (entity->GetVar<bool>(u"noGhosting")) entity->SetIsGhostingCandidate(false);
|
if (entity->GetVar<bool>(u"noGhosting")) entity->SetIsGhostingCandidate(false);
|
||||||
|
|
||||||
// Debug print
|
|
||||||
LOG_DEBUG("Spawning NPC %s (%i) at %f, %f, %f", name.c_str(), lot, position.x, position.y, position.z);
|
|
||||||
|
|
||||||
auto* inventoryComponent = entity->GetComponent<InventoryComponent>();
|
auto* inventoryComponent = entity->GetComponent<InventoryComponent>();
|
||||||
|
|
||||||
if (inventoryComponent && !inventory.empty()) {
|
if (inventoryComponent && !inventory.empty()) {
|
||||||
@ -184,15 +217,6 @@ Entity* VanityUtilities::SpawnNPC(LOT lot, const std::string& name, const NiPoin
|
|||||||
destroyableComponent->SetHealth(0);
|
destroyableComponent->SetHealth(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto* scriptComponent = entity->GetComponent<ScriptComponent>();
|
|
||||||
|
|
||||||
if (scriptComponent == nullptr)
|
|
||||||
{
|
|
||||||
entity->AddComponent<ScriptComponent>("", false);
|
|
||||||
}
|
|
||||||
|
|
||||||
LOG_DEBUG("NPC has script component? %s", (entity->GetComponent<ScriptComponent>() != nullptr) ? "true" : "false");
|
|
||||||
|
|
||||||
Game::entityManager->ConstructEntity(entity);
|
Game::entityManager->ConstructEntity(entity);
|
||||||
|
|
||||||
return entity;
|
return entity;
|
||||||
|
@ -13,6 +13,7 @@ struct VanityNPCLocation
|
|||||||
|
|
||||||
struct VanityNPC
|
struct VanityNPC
|
||||||
{
|
{
|
||||||
|
LWOOBJID m_ID = LWOOBJID_EMPTY;
|
||||||
std::string m_Name;
|
std::string m_Name;
|
||||||
LOT m_LOT;
|
LOT m_LOT;
|
||||||
std::vector<LOT> m_Equipment;
|
std::vector<LOT> m_Equipment;
|
||||||
@ -44,6 +45,13 @@ public:
|
|||||||
const std::vector<LDFBaseData*>& ldf
|
const std::vector<LDFBaseData*>& ldf
|
||||||
);
|
);
|
||||||
|
|
||||||
|
static LWOOBJID SpawnSpawner(
|
||||||
|
LOT lot,
|
||||||
|
const NiPoint3& position,
|
||||||
|
const NiQuaternion& rotation,
|
||||||
|
const std::vector<LDFBaseData*>& ldf
|
||||||
|
);
|
||||||
|
|
||||||
static std::string ParseMarkdown(
|
static std::string ParseMarkdown(
|
||||||
const std::string& file
|
const std::string& file
|
||||||
);
|
);
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
#include "PerformanceManager.h"
|
#include "PerformanceManager.h"
|
||||||
|
#include "CDZoneTableTable.h"
|
||||||
|
#include "CDClientManager.h"
|
||||||
#include "UserManager.h"
|
#include "UserManager.h"
|
||||||
|
|
||||||
#define SOCIAL { lowFrameDelta }
|
#define SOCIAL { lowFrameDelta }
|
||||||
@ -68,11 +69,30 @@ std::map<LWOMAPID, PerformanceProfile> PerformanceManager::m_Profiles = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
void PerformanceManager::SelectProfile(LWOMAPID mapID) {
|
void PerformanceManager::SelectProfile(LWOMAPID mapID) {
|
||||||
const auto pair = m_Profiles.find(mapID);
|
// Try to get it from zoneTable
|
||||||
|
CDZoneTableTable* zoneTable = CDClientManager::Instance().GetTable<CDZoneTableTable>();
|
||||||
|
if (zoneTable) {
|
||||||
|
const CDZoneTable* zone = zoneTable->Query(mapID);
|
||||||
|
if (zone) {
|
||||||
|
if (zone->serverPhysicsFramerate == "high"){
|
||||||
|
m_CurrentProfile = { highFrameDelta };
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (zone->serverPhysicsFramerate == "medium"){
|
||||||
|
m_CurrentProfile = { mediumFrameDelta };
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (zone->serverPhysicsFramerate == "low"){
|
||||||
|
m_CurrentProfile = { lowFrameDelta };
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fall back to hardcoded list and defaults
|
||||||
|
const auto pair = m_Profiles.find(mapID);
|
||||||
if (pair == m_Profiles.end()) {
|
if (pair == m_Profiles.end()) {
|
||||||
m_CurrentProfile = m_DefaultProfile;
|
m_CurrentProfile = m_DefaultProfile;
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -38,6 +38,76 @@ Level::~Level() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Level::MakeSpawner(SceneObject obj){
|
||||||
|
SpawnerInfo spawnInfo = SpawnerInfo();
|
||||||
|
SpawnerNode* node = new SpawnerNode();
|
||||||
|
spawnInfo.templateID = obj.lot;
|
||||||
|
spawnInfo.spawnerID = obj.id;
|
||||||
|
spawnInfo.templateScale = obj.scale;
|
||||||
|
node->position = obj.position;
|
||||||
|
node->rotation = obj.rotation;
|
||||||
|
node->config = obj.settings;
|
||||||
|
spawnInfo.nodes.push_back(node);
|
||||||
|
for (LDFBaseData* data : obj.settings) {
|
||||||
|
if (data) {
|
||||||
|
if (data->GetKey() == u"spawntemplate") {
|
||||||
|
spawnInfo.templateID = std::stoi(data->GetValueAsString());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data->GetKey() == u"spawner_node_id") {
|
||||||
|
node->nodeID = std::stoi(data->GetValueAsString());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data->GetKey() == u"spawner_name") {
|
||||||
|
spawnInfo.name = data->GetValueAsString();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data->GetKey() == u"max_to_spawn") {
|
||||||
|
spawnInfo.maxToSpawn = std::stoi(data->GetValueAsString());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data->GetKey() == u"spawner_active_on_load") {
|
||||||
|
spawnInfo.activeOnLoad = std::stoi(data->GetValueAsString());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data->GetKey() == u"active_on_load") {
|
||||||
|
spawnInfo.activeOnLoad = std::stoi(data->GetValueAsString());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data->GetKey() == u"respawn") {
|
||||||
|
if (data->GetValueType() == eLDFType::LDF_TYPE_FLOAT) // Floats are in seconds
|
||||||
|
{
|
||||||
|
spawnInfo.respawnTime = std::stof(data->GetValueAsString());
|
||||||
|
} else if (data->GetValueType() == eLDFType::LDF_TYPE_U32) // Ints are in ms?
|
||||||
|
{
|
||||||
|
spawnInfo.respawnTime = std::stoul(data->GetValueAsString()) / 1000;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (data->GetKey() == u"spawnsGroupOnSmash") {
|
||||||
|
spawnInfo.spawnsOnSmash = std::stoi(data->GetValueAsString());
|
||||||
|
}
|
||||||
|
if (data->GetKey() == u"spawnNetNameForSpawnGroupOnSmash") {
|
||||||
|
spawnInfo.spawnOnSmashGroupName = data->GetValueAsString();
|
||||||
|
}
|
||||||
|
if (data->GetKey() == u"groupID") { // Load object groups
|
||||||
|
std::string groupStr = data->GetValueAsString();
|
||||||
|
spawnInfo.groups = GeneralUtils::SplitString(groupStr, ';');
|
||||||
|
spawnInfo.groups.erase(spawnInfo.groups.end() - 1);
|
||||||
|
}
|
||||||
|
if (data->GetKey() == u"no_auto_spawn") {
|
||||||
|
spawnInfo.noAutoSpawn = static_cast<LDFData<bool>*>(data)->GetValue();
|
||||||
|
}
|
||||||
|
if (data->GetKey() == u"no_timed_spawn") {
|
||||||
|
spawnInfo.noTimedSpawn = static_cast<LDFData<bool>*>(data)->GetValue();
|
||||||
|
}
|
||||||
|
if (data->GetKey() == u"spawnActivator") {
|
||||||
|
spawnInfo.spawnActivator = static_cast<LDFData<bool>*>(data)->GetValue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Game::zoneManager->MakeSpawner(spawnInfo);
|
||||||
|
}
|
||||||
|
|
||||||
const void Level::PrintAllObjects() {
|
const void Level::PrintAllObjects() {
|
||||||
for (std::map<uint32_t, Header>::iterator it = m_ChunkHeaders.begin(); it != m_ChunkHeaders.end(); ++it) {
|
for (std::map<uint32_t, Header>::iterator it = m_ChunkHeaders.begin(); it != m_ChunkHeaders.end(); ++it) {
|
||||||
if (it->second.id == Level::ChunkTypeID::SceneObjectData) {
|
if (it->second.id == Level::ChunkTypeID::SceneObjectData) {
|
||||||
@ -230,74 +300,7 @@ void Level::ReadSceneObjectDataChunk(std::istream& file, Header& header) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (obj.lot == 176) { //Spawner
|
if (obj.lot == 176) { //Spawner
|
||||||
SpawnerInfo spawnInfo = SpawnerInfo();
|
MakeSpawner(obj);
|
||||||
SpawnerNode* node = new SpawnerNode();
|
|
||||||
spawnInfo.templateID = obj.lot;
|
|
||||||
spawnInfo.spawnerID = obj.id;
|
|
||||||
spawnInfo.templateScale = obj.scale;
|
|
||||||
node->position = obj.position;
|
|
||||||
node->rotation = obj.rotation;
|
|
||||||
node->config = obj.settings;
|
|
||||||
spawnInfo.nodes.push_back(node);
|
|
||||||
for (LDFBaseData* data : obj.settings) {
|
|
||||||
if (data) {
|
|
||||||
if (data->GetKey() == u"spawntemplate") {
|
|
||||||
spawnInfo.templateID = std::stoi(data->GetValueAsString());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (data->GetKey() == u"spawner_node_id") {
|
|
||||||
node->nodeID = std::stoi(data->GetValueAsString());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (data->GetKey() == u"spawner_name") {
|
|
||||||
spawnInfo.name = data->GetValueAsString();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (data->GetKey() == u"max_to_spawn") {
|
|
||||||
spawnInfo.maxToSpawn = std::stoi(data->GetValueAsString());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (data->GetKey() == u"spawner_active_on_load") {
|
|
||||||
spawnInfo.activeOnLoad = std::stoi(data->GetValueAsString());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (data->GetKey() == u"active_on_load") {
|
|
||||||
spawnInfo.activeOnLoad = std::stoi(data->GetValueAsString());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (data->GetKey() == u"respawn") {
|
|
||||||
if (data->GetValueType() == eLDFType::LDF_TYPE_FLOAT) // Floats are in seconds
|
|
||||||
{
|
|
||||||
spawnInfo.respawnTime = std::stof(data->GetValueAsString());
|
|
||||||
} else if (data->GetValueType() == eLDFType::LDF_TYPE_U32) // Ints are in ms?
|
|
||||||
{
|
|
||||||
spawnInfo.respawnTime = std::stoul(data->GetValueAsString()) / 1000;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (data->GetKey() == u"spawnsGroupOnSmash") {
|
|
||||||
spawnInfo.spawnsOnSmash = std::stoi(data->GetValueAsString());
|
|
||||||
}
|
|
||||||
if (data->GetKey() == u"spawnNetNameForSpawnGroupOnSmash") {
|
|
||||||
spawnInfo.spawnOnSmashGroupName = data->GetValueAsString();
|
|
||||||
}
|
|
||||||
if (data->GetKey() == u"groupID") { // Load object groups
|
|
||||||
std::string groupStr = data->GetValueAsString();
|
|
||||||
spawnInfo.groups = GeneralUtils::SplitString(groupStr, ';');
|
|
||||||
spawnInfo.groups.erase(spawnInfo.groups.end() - 1);
|
|
||||||
}
|
|
||||||
if (data->GetKey() == u"no_auto_spawn") {
|
|
||||||
spawnInfo.noAutoSpawn = static_cast<LDFData<bool>*>(data)->GetValue();
|
|
||||||
}
|
|
||||||
if (data->GetKey() == u"no_timed_spawn") {
|
|
||||||
spawnInfo.noTimedSpawn = static_cast<LDFData<bool>*>(data)->GetValue();
|
|
||||||
}
|
|
||||||
if (data->GetKey() == u"spawnActivator") {
|
|
||||||
spawnInfo.spawnActivator = static_cast<LDFData<bool>*>(data)->GetValue();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Spawner* spawner = new Spawner(spawnInfo);
|
|
||||||
Game::zoneManager->AddSpawner(obj.id, spawner);
|
|
||||||
} else { //Regular object
|
} else { //Regular object
|
||||||
EntityInfo info;
|
EntityInfo info;
|
||||||
info.spawnerID = 0;
|
info.spawnerID = 0;
|
||||||
|
@ -53,6 +53,8 @@ public:
|
|||||||
public:
|
public:
|
||||||
Level(Zone* parentZone, const std::string& filepath);
|
Level(Zone* parentZone, const std::string& filepath);
|
||||||
~Level();
|
~Level();
|
||||||
|
|
||||||
|
static void MakeSpawner(SceneObject obj);
|
||||||
|
|
||||||
const void PrintAllObjects();
|
const void PrintAllObjects();
|
||||||
|
|
||||||
|
@ -40,6 +40,9 @@ void dZoneManager::Initialize(const LWOZONEID& zoneID) {
|
|||||||
Game::entityManager->SetGhostDistanceMax(max + min);
|
Game::entityManager->SetGhostDistanceMax(max + min);
|
||||||
Game::entityManager->SetGhostDistanceMin(max);
|
Game::entityManager->SetGhostDistanceMin(max);
|
||||||
m_PlayerLoseCoinsOnDeath = zone->PlayerLoseCoinsOnDeath;
|
m_PlayerLoseCoinsOnDeath = zone->PlayerLoseCoinsOnDeath;
|
||||||
|
m_DisableSaveLocation = zone->disableSaveLoc;
|
||||||
|
m_MountsAllowed = zone->mountsAllowed;
|
||||||
|
m_PetsAllowed = zone->petsAllowed;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -41,6 +41,9 @@ public:
|
|||||||
void Update(float deltaTime);
|
void Update(float deltaTime);
|
||||||
Entity* GetZoneControlObject() { return m_ZoneControlObject; }
|
Entity* GetZoneControlObject() { return m_ZoneControlObject; }
|
||||||
bool GetPlayerLoseCoinOnDeath() { return m_PlayerLoseCoinsOnDeath; }
|
bool GetPlayerLoseCoinOnDeath() { return m_PlayerLoseCoinsOnDeath; }
|
||||||
|
bool GetDisableSaveLocation() { return m_DisableSaveLocation; }
|
||||||
|
bool GetMountsAllowed() { return m_MountsAllowed; }
|
||||||
|
bool GetPetsAllowed() { return m_PetsAllowed; }
|
||||||
uint32_t GetUniqueMissionIdStartingValue();
|
uint32_t GetUniqueMissionIdStartingValue();
|
||||||
bool CheckIfAccessibleZone(LWOMAPID zoneID);
|
bool CheckIfAccessibleZone(LWOMAPID zoneID);
|
||||||
|
|
||||||
@ -58,7 +61,10 @@ private:
|
|||||||
|
|
||||||
Zone* m_pZone = nullptr;
|
Zone* m_pZone = nullptr;
|
||||||
LWOZONEID m_ZoneID;
|
LWOZONEID m_ZoneID;
|
||||||
bool m_PlayerLoseCoinsOnDeath; //Do players drop coins in this zone when smashed
|
bool m_PlayerLoseCoinsOnDeath = false;
|
||||||
|
bool m_DisableSaveLocation = false;
|
||||||
|
bool m_MountsAllowed = true;
|
||||||
|
bool m_PetsAllowed = true;
|
||||||
std::map<LWOOBJID, Spawner*> m_Spawners;
|
std::map<LWOOBJID, Spawner*> m_Spawners;
|
||||||
WorldConfig* m_WorldConfig = nullptr;
|
WorldConfig* m_WorldConfig = nullptr;
|
||||||
|
|
||||||
|
@ -25,6 +25,7 @@
|
|||||||
|ban|`/ban <username>`|Bans a user from the server.|4|
|
|ban|`/ban <username>`|Bans a user from the server.|4|
|
||||||
|approveproperty|`/approveproperty`|Approves the property the player is currently visiting.|5|
|
|approveproperty|`/approveproperty`|Approves the property the player is currently visiting.|5|
|
||||||
|mute|`/mute <username> (days) (hours)`|Mute player for the given amount of time. If no time is given, the mute is indefinite.|6|
|
|mute|`/mute <username> (days) (hours)`|Mute player for the given amount of time. If no time is given, the mute is indefinite.|6|
|
||||||
|
|attackimmune|`/attackimmune <value>`|Sets the character's immunity to basic attacks state, where value can be one of "1", to make yourself immune to basic attack damage, or "0" to undo.|8|
|
||||||
|gmimmune|`/gmimmunve <value>`|Sets the character's GMImmune state, where value can be one of "1", to make yourself immune to damage, or "0" to undo.|8|
|
|gmimmune|`/gmimmunve <value>`|Sets the character's GMImmune state, where value can be one of "1", to make yourself immune to damage, or "0" to undo.|8|
|
||||||
|gminvis|`/gminvis`|Toggles invisibility for the character, though it's currently a bit buggy. Requires nonzero GM Level for the character, but the account must have a GM level of 8.|8|
|
|gminvis|`/gminvis`|Toggles invisibility for the character, though it's currently a bit buggy. Requires nonzero GM Level for the character, but the account must have a GM level of 8.|8|
|
||||||
|setname|`/setname <name>`|Sets a temporary name for your player. The name resets when you log out.|8|
|
|setname|`/setname <name>`|Sets a temporary name for your player. The name resets when you log out.|8|
|
||||||
|
@ -536,3 +536,22 @@ TEST_F(DestroyableTest, DestroyableComponentImmunityTest) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test the Damage cooldown timer of DestroyableComponent
|
||||||
|
*/
|
||||||
|
TEST_F(DestroyableTest, DestroyableComponentDamageCooldownTest) {
|
||||||
|
// Test the damage immune timer state (anything above 0.0f)
|
||||||
|
destroyableComponent->SetDamageCooldownTimer(1.0f);
|
||||||
|
EXPECT_FLOAT_EQ(destroyableComponent->GetDamageCooldownTimer(), 1.0f);
|
||||||
|
ASSERT_TRUE(destroyableComponent->IsCooldownImmune());
|
||||||
|
|
||||||
|
// Test that the Update() function correctly decrements the damage cooldown timer
|
||||||
|
destroyableComponent->Update(0.5f);
|
||||||
|
EXPECT_FLOAT_EQ(destroyableComponent->GetDamageCooldownTimer(), 0.5f);
|
||||||
|
ASSERT_TRUE(destroyableComponent->IsCooldownImmune());
|
||||||
|
|
||||||
|
// Test the non damage immune timer state (anything below or equal to 0.0f)
|
||||||
|
destroyableComponent->SetDamageCooldownTimer(0.0f);
|
||||||
|
EXPECT_FLOAT_EQ(destroyableComponent->GetDamageCooldownTimer(), 0.0f);
|
||||||
|
ASSERT_FALSE(destroyableComponent->IsCooldownImmune());
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user