mirror of
https://github.com/DarkflameUniverse/DarkflameServer.git
synced 2026-06-13 18:24:20 +00:00
Compare commits
9 Commits
tree-defen
...
rotation-b
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6e201761cc | ||
|
|
a70c365c23 | ||
|
|
281d9762ef | ||
|
|
002aa896d8 | ||
|
|
f3a5f60d81 | ||
|
|
4c9c773ec5 | ||
|
|
d6a21a005f | ||
|
|
fccf2fb0d3 | ||
|
|
4d043398ab |
@@ -374,6 +374,21 @@ public:
|
|||||||
return value->Insert<AmfType>("value", std::make_unique<AmfType>());
|
return value->Insert<AmfType>("value", std::make_unique<AmfType>());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
AMFArrayValue& PushDebug(const NiPoint3& point) {
|
||||||
|
PushDebug<AMFDoubleValue>("X") = point.x;
|
||||||
|
PushDebug<AMFDoubleValue>("Y") = point.y;
|
||||||
|
PushDebug<AMFDoubleValue>("Z") = point.z;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
AMFArrayValue& PushDebug(const NiQuaternion& rot) {
|
||||||
|
PushDebug<AMFDoubleValue>("W") = rot.w;
|
||||||
|
PushDebug<AMFDoubleValue>("X") = rot.x;
|
||||||
|
PushDebug<AMFDoubleValue>("Y") = rot.y;
|
||||||
|
PushDebug<AMFDoubleValue>("Z") = rot.z;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/**
|
/**
|
||||||
* The associative portion. These values are key'd with strings to an AMFValue.
|
* The associative portion. These values are key'd with strings to an AMFValue.
|
||||||
|
|||||||
@@ -11,6 +11,65 @@ Vector3 QuatUtils::Euler(const NiQuaternion& quat) {
|
|||||||
return glm::eulerAngles(quat);
|
return glm::eulerAngles(quat);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
NiQuaternion NiQuaternion::operator*(const float scalar) const noexcept {
|
||||||
|
return NiQuaternion(this->w * scalar, this->x * scalar, this->y * scalar, this->z * scalar);
|
||||||
|
}
|
||||||
|
|
||||||
|
NiQuaternion& NiQuaternion::operator*=(const NiQuaternion& q) {
|
||||||
|
auto& [ow, ox, oy, oz] = q;
|
||||||
|
auto [cw, cx, cy, cz] = *this; // Current rotation copied because otherwise it screws up the math
|
||||||
|
this->w = cw * ow - cx * ox - cy * oy - cz * oz;
|
||||||
|
this->x = cw * ox + cx * ow + cy * oz - cz * oy;
|
||||||
|
this->y = cw * oy + cy * ow + cz * ox - cx * oz;
|
||||||
|
this->z = cw * oz + cz * ow + cx * oy - cy * ox;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
NiQuaternion NiQuaternion::operator* (const NiQuaternion& q) const {
|
||||||
|
auto& [ow, ox, oy, oz] = q;
|
||||||
|
return NiQuaternion
|
||||||
|
(
|
||||||
|
/* w */w * ow - x * ox - y * oy - z * oz,
|
||||||
|
/* x */w * ox + x * ow + y * oz - z * oy,
|
||||||
|
/* y */w * oy + y * ow + z * ox - x * oz,
|
||||||
|
/* z */w * oz + z * ow + x * oy - y * ox
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
NiQuaternion NiQuaternion::operator/(const float& q) const noexcept {
|
||||||
|
return NiQuaternion(this->w / q, this->x / q, this->y / q, this->z / q);
|
||||||
|
}
|
||||||
|
|
||||||
|
void NiQuaternion::Normalize() {
|
||||||
|
float length = Dot(*this);
|
||||||
|
float invLength = 1.0f / std::sqrt(length);
|
||||||
|
*this = *this * invLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
float NiQuaternion::Dot(const NiQuaternion& q) const noexcept {
|
||||||
|
return (this->w * q.w) + (this->x * q.x) + (this->y * q.y) + (this->z * q.z);
|
||||||
|
}
|
||||||
|
|
||||||
|
void NiQuaternion::Inverse() noexcept {
|
||||||
|
NiQuaternion copy = *this;
|
||||||
|
copy.Conjugate();
|
||||||
|
|
||||||
|
const float inv = 1.0f / Dot(*this);
|
||||||
|
*this = copy / inv;
|
||||||
|
}
|
||||||
|
|
||||||
|
void NiQuaternion::Conjugate() noexcept {
|
||||||
|
x = -x;
|
||||||
|
y = -y;
|
||||||
|
z = -z;
|
||||||
|
}
|
||||||
|
|
||||||
|
NiQuaternion NiQuaternion::Diff(const NiQuaternion& q) const noexcept {
|
||||||
|
NiQuaternion inv = *this;
|
||||||
|
inv.Inverse();
|
||||||
|
return inv * q;
|
||||||
|
}
|
||||||
|
|
||||||
// MARK: Helper Functions
|
// MARK: Helper Functions
|
||||||
|
|
||||||
//! Look from a specific point in space to another point in space (Y-locked)
|
//! Look from a specific point in space to another point in space (Y-locked)
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ constexpr LWOINSTANCEID LWOINSTANCEID_INVALID = -1; //!< Invalid LWOINSTANCEID
|
|||||||
constexpr LWOMAPID LWOMAPID_INVALID = -1; //!< Invalid LWOMAPID
|
constexpr LWOMAPID LWOMAPID_INVALID = -1; //!< Invalid LWOMAPID
|
||||||
constexpr uint64_t LWOZONEID_INVALID = 0; //!< Invalid LWOZONEID
|
constexpr uint64_t LWOZONEID_INVALID = 0; //!< Invalid LWOZONEID
|
||||||
|
|
||||||
constexpr float PI = 3.14159f;
|
constexpr float PI = 3.14159265358979323846264338327950288f;
|
||||||
|
|
||||||
//============ STRUCTS ==============
|
//============ STRUCTS ==============
|
||||||
|
|
||||||
|
|||||||
23
dCommon/dMath.h
Normal file
23
dCommon/dMath.h
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
// Darkflame Universe
|
||||||
|
// Copyright 2025
|
||||||
|
|
||||||
|
#ifndef DMATH_H
|
||||||
|
#define DMATH_H
|
||||||
|
|
||||||
|
#include <cmath>
|
||||||
|
|
||||||
|
namespace Math {
|
||||||
|
constexpr float PI = 3.14159265358979323846264338327950288f;
|
||||||
|
constexpr float RATIO_DEG_TO_RAD = PI / 180.0f;
|
||||||
|
constexpr float RATIO_RAD_TO_DEG = 180.0f / PI;
|
||||||
|
|
||||||
|
inline float DegToRad(float degrees) {
|
||||||
|
return degrees * RATIO_DEG_TO_RAD;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline float RadToDeg(float radians) {
|
||||||
|
return radians * RATIO_RAD_TO_DEG;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif //!DMATH_H
|
||||||
@@ -424,6 +424,7 @@ void Entity::Initialize() {
|
|||||||
comp->SetIsSmashable(destCompData[0].isSmashable);
|
comp->SetIsSmashable(destCompData[0].isSmashable);
|
||||||
|
|
||||||
comp->SetLootMatrixID(destCompData[0].LootMatrixIndex);
|
comp->SetLootMatrixID(destCompData[0].LootMatrixIndex);
|
||||||
|
comp->SetCurrencyIndex(destCompData[0].CurrencyIndex);
|
||||||
Loot::CacheMatrix(destCompData[0].LootMatrixIndex);
|
Loot::CacheMatrix(destCompData[0].LootMatrixIndex);
|
||||||
|
|
||||||
// Now get currency information
|
// Now get currency information
|
||||||
@@ -2252,6 +2253,7 @@ bool Entity::MsgRequestServerObjectInfo(GameMessages::GameMsg& msg) {
|
|||||||
response.Insert("objectID", std::to_string(m_ObjectID));
|
response.Insert("objectID", std::to_string(m_ObjectID));
|
||||||
response.Insert("serverInfo", true);
|
response.Insert("serverInfo", true);
|
||||||
GameMessages::GetObjectReportInfo info{};
|
GameMessages::GetObjectReportInfo info{};
|
||||||
|
info.clientID = requestInfo.clientId;
|
||||||
info.bVerbose = requestInfo.bVerbose;
|
info.bVerbose = requestInfo.bVerbose;
|
||||||
info.info = response.InsertArray("data");
|
info.info = response.InsertArray("data");
|
||||||
auto& objectInfo = info.info->PushDebug("Object Details");
|
auto& objectInfo = info.info->PushDebug("Object Details");
|
||||||
|
|||||||
@@ -114,7 +114,6 @@ void TacArcBehavior::Calculate(BehaviorContext* context, RakNet::BitStream& bitS
|
|||||||
context->FilterTargets(validTargets, this->m_ignoreFactionList, this->m_includeFactionList, this->m_targetSelf, this->m_targetEnemy, this->m_targetFriend, this->m_targetTeam);
|
context->FilterTargets(validTargets, this->m_ignoreFactionList, this->m_includeFactionList, this->m_targetSelf, this->m_targetEnemy, this->m_targetFriend, this->m_targetTeam);
|
||||||
|
|
||||||
for (auto validTarget : validTargets) {
|
for (auto validTarget : validTargets) {
|
||||||
if (targets.size() >= this->m_maxTargets) break;
|
|
||||||
if (std::find(targets.begin(), targets.end(), validTarget) != targets.end()) continue;
|
if (std::find(targets.begin(), targets.end(), validTarget) != targets.end()) continue;
|
||||||
if (validTarget->GetIsDead()) continue;
|
if (validTarget->GetIsDead()) continue;
|
||||||
|
|
||||||
@@ -147,13 +146,28 @@ void TacArcBehavior::Calculate(BehaviorContext* context, RakNet::BitStream& bitS
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::sort(targets.begin(), targets.end(), [reference](Entity* a, Entity* b) {
|
std::sort(targets.begin(), targets.end(), [this, reference, combatAi](Entity* a, Entity* b) {
|
||||||
const auto aDistance = Vector3::DistanceSquared(reference, a->GetPosition());
|
const auto aDistance = Vector3::DistanceSquared(reference, a->GetPosition());
|
||||||
const auto bDistance = Vector3::DistanceSquared(reference, b->GetPosition());
|
const auto bDistance = Vector3::DistanceSquared(reference, b->GetPosition());
|
||||||
|
|
||||||
return aDistance > bDistance;
|
return aDistance < bDistance;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
if (m_useAttackPriority) {
|
||||||
|
// this should be using the attack priority column on the destroyable component
|
||||||
|
// We want targets with no threat level to remain the same order as above
|
||||||
|
// std::stable_sort(targets.begin(), targets.end(), [combatAi](Entity* a, Entity* b) {
|
||||||
|
// const auto aThreat = combatAi->GetThreat(a->GetObjectID());
|
||||||
|
// const auto bThreat = combatAi->GetThreat(b->GetObjectID());
|
||||||
|
|
||||||
|
// If enabled for this behavior, prioritize threat over distance
|
||||||
|
// return aThreat > bThreat;
|
||||||
|
// });
|
||||||
|
}
|
||||||
|
|
||||||
|
// After we've sorted and found our closest targets, size the vector down in case there are too many
|
||||||
|
if (m_maxTargets > 0 && targets.size() > m_maxTargets) targets.resize(m_maxTargets);
|
||||||
const auto hit = !targets.empty();
|
const auto hit = !targets.empty();
|
||||||
bitStream.Write(hit);
|
bitStream.Write(hit);
|
||||||
|
|
||||||
|
|||||||
@@ -27,8 +27,13 @@
|
|||||||
#include "CDComponentsRegistryTable.h"
|
#include "CDComponentsRegistryTable.h"
|
||||||
#include "CDPhysicsComponentTable.h"
|
#include "CDPhysicsComponentTable.h"
|
||||||
#include "dNavMesh.h"
|
#include "dNavMesh.h"
|
||||||
|
#include "Amf3.h"
|
||||||
|
|
||||||
BaseCombatAIComponent::BaseCombatAIComponent(Entity* parent, const int32_t componentID) : Component(parent, componentID) {
|
BaseCombatAIComponent::BaseCombatAIComponent(Entity* parent, const int32_t componentID) : Component(parent, componentID) {
|
||||||
|
{
|
||||||
|
using namespace GameMessages;
|
||||||
|
RegisterMsg<GetObjectReportInfo>(this, &BaseCombatAIComponent::MsgGetObjectReportInfo);
|
||||||
|
}
|
||||||
m_Target = LWOOBJID_EMPTY;
|
m_Target = LWOOBJID_EMPTY;
|
||||||
m_DirtyStateOrTarget = true;
|
m_DirtyStateOrTarget = true;
|
||||||
m_State = AiState::spawn;
|
m_State = AiState::spawn;
|
||||||
@@ -839,3 +844,73 @@ void BaseCombatAIComponent::IgnoreThreat(const LWOOBJID threat, const float valu
|
|||||||
SetThreat(threat, 0.0f);
|
SetThreat(threat, 0.0f);
|
||||||
m_Target = LWOOBJID_EMPTY;
|
m_Target = LWOOBJID_EMPTY;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool BaseCombatAIComponent::MsgGetObjectReportInfo(GameMessages::GameMsg& msg) {
|
||||||
|
using enum AiState;
|
||||||
|
auto& reportMsg = static_cast<GameMessages::GetObjectReportInfo&>(msg);
|
||||||
|
auto& cmptType = reportMsg.info->PushDebug("Base Combat AI");
|
||||||
|
cmptType.PushDebug<AMFIntValue>("Component ID") = GetComponentID();
|
||||||
|
auto& targetInfo = cmptType.PushDebug("Current Target Info");
|
||||||
|
targetInfo.PushDebug<AMFStringValue>("Current Target ID") = std::to_string(m_Target);
|
||||||
|
// if (m_Target != LWOOBJID_EMPTY) {
|
||||||
|
// LWOGameMessages::ObjGetName nameMsg(m_CurrentTarget);
|
||||||
|
// SEND_GAMEOBJ_MSG(nameMsg);
|
||||||
|
// if (!nameMsg.msg.name.empty()) targetInfo.PushDebug("Name") = nameMsg.msg.name;
|
||||||
|
// }
|
||||||
|
|
||||||
|
auto& roundInfo = cmptType.PushDebug("Round Info");
|
||||||
|
// roundInfo.PushDebug<AMFDoubleValue>("Combat Round Time") = m_CombatRoundLength;
|
||||||
|
// roundInfo.PushDebug<AMFDoubleValue>("Minimum Time") = m_MinRoundLength;
|
||||||
|
// roundInfo.PushDebug<AMFDoubleValue>("Maximum Time") = m_MaxRoundLength;
|
||||||
|
// roundInfo.PushDebug<AMFDoubleValue>("Selected Time") = m_SelectedTime;
|
||||||
|
// roundInfo.PushDebug<AMFDoubleValue>("Combat Start Delay") = m_CombatStartDelay;
|
||||||
|
std::string curState;
|
||||||
|
switch (m_State) {
|
||||||
|
case idle: curState = "Idling"; break;
|
||||||
|
case aggro: curState = "Aggroed"; break;
|
||||||
|
case tether: curState = "Returning to Tether"; break;
|
||||||
|
case spawn: curState = "Spawn"; break;
|
||||||
|
case dead: curState = "Dead"; break;
|
||||||
|
default: curState = "Unknown or Undefined"; break;
|
||||||
|
}
|
||||||
|
cmptType.PushDebug<AMFStringValue>("Current Combat State") = curState;
|
||||||
|
|
||||||
|
//switch (m_CombatBehaviorType) {
|
||||||
|
// case 0: curState = "Passive"; break;
|
||||||
|
// case 1: curState = "Aggressive"; break;
|
||||||
|
// case 2: curState = "Passive (Turret)"; break;
|
||||||
|
// case 3: curState = "Aggressive (Turret)"; break;
|
||||||
|
// default: curState = "Unknown or Undefined"; break;
|
||||||
|
//}
|
||||||
|
//cmptType.PushDebug("Current Combat Behavior State") = curState;
|
||||||
|
|
||||||
|
//switch (m_CombatRole) {
|
||||||
|
// case 0: curState = "Melee"; break;
|
||||||
|
// case 1: curState = "Ranged"; break;
|
||||||
|
// case 2: curState = "Support"; break;
|
||||||
|
// default: curState = "Unknown or Undefined"; break;
|
||||||
|
//}
|
||||||
|
//cmptType.PushDebug("Current Combat Role") = curState;
|
||||||
|
|
||||||
|
auto& tetherPoint = cmptType.PushDebug("Tether Point");
|
||||||
|
tetherPoint.PushDebug<AMFDoubleValue>("X") = m_StartPosition.x;
|
||||||
|
tetherPoint.PushDebug<AMFDoubleValue>("Y") = m_StartPosition.y;
|
||||||
|
tetherPoint.PushDebug<AMFDoubleValue>("Z") = m_StartPosition.z;
|
||||||
|
cmptType.PushDebug<AMFDoubleValue>("Hard Tether Radius") = m_HardTetherRadius;
|
||||||
|
cmptType.PushDebug<AMFDoubleValue>("Soft Tether Radius") = m_SoftTetherRadius;
|
||||||
|
cmptType.PushDebug<AMFDoubleValue>("Aggro Radius") = m_AggroRadius;
|
||||||
|
cmptType.PushDebug<AMFDoubleValue>("Tether Speed") = m_TetherSpeed;
|
||||||
|
cmptType.PushDebug<AMFDoubleValue>("Aggro Speed") = m_TetherSpeed;
|
||||||
|
// cmptType.PushDebug<AMFDoubleValue>("Specified Min Range") = m_SpecificMinRange;
|
||||||
|
// cmptType.PushDebug<AMFDoubleValue>("Specified Max Range") = m_SpecificMaxRange;
|
||||||
|
auto& threats = cmptType.PushDebug("Target Threats");
|
||||||
|
for (const auto& [id, threat] : m_ThreatEntries) {
|
||||||
|
threats.PushDebug<AMFDoubleValue>(std::to_string(id)) = threat;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto& ignoredThreats = cmptType.PushDebug("Temp Ignored Threats");
|
||||||
|
for (const auto& [id, threat] : m_ThreatEntries) {
|
||||||
|
ignoredThreats.PushDebug<AMFDoubleValue>(std::to_string(id) + " - Time") = threat;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|||||||
@@ -234,6 +234,8 @@ public:
|
|||||||
// Ignore a threat for a certain amount of time
|
// Ignore a threat for a certain amount of time
|
||||||
void IgnoreThreat(const LWOOBJID target, const float time);
|
void IgnoreThreat(const LWOOBJID target, const float time);
|
||||||
|
|
||||||
|
bool MsgGetObjectReportInfo(GameMessages::GameMsg& msg);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/**
|
/**
|
||||||
* Returns the current target or the target that currently is the largest threat to this entity
|
* Returns the current target or the target that currently is the largest threat to this entity
|
||||||
|
|||||||
@@ -8,15 +8,33 @@
|
|||||||
#include "GameMessages.h"
|
#include "GameMessages.h"
|
||||||
#include "BitStream.h"
|
#include "BitStream.h"
|
||||||
#include "eTriggerEventType.h"
|
#include "eTriggerEventType.h"
|
||||||
|
#include "Amf3.h"
|
||||||
|
|
||||||
BouncerComponent::BouncerComponent(Entity* parent, const int32_t componentID) : Component(parent, componentID) {
|
BouncerComponent::BouncerComponent(Entity* parent, const int32_t componentID) : Component(parent, componentID) {
|
||||||
m_PetEnabled = false;
|
m_PetEnabled = false;
|
||||||
m_PetBouncerEnabled = false;
|
m_PetBouncerEnabled = false;
|
||||||
m_PetSwitchLoaded = false;
|
m_PetSwitchLoaded = false;
|
||||||
|
m_Destination = GeneralUtils::TryParse<NiPoint3>(
|
||||||
|
GeneralUtils::SplitString(m_Parent->GetVarAsString(u"bouncer_destination"), '\x1f'))
|
||||||
|
.value_or(NiPoint3Constant::ZERO);
|
||||||
|
m_Speed = GeneralUtils::TryParse<float>(m_Parent->GetVarAsString(u"bouncer_speed")).value_or(-1.0f);
|
||||||
|
m_UsesHighArc = GeneralUtils::TryParse<bool>(m_Parent->GetVarAsString(u"bouncer_uses_high_arc")).value_or(false);
|
||||||
|
m_LockControls = GeneralUtils::TryParse<bool>(m_Parent->GetVarAsString(u"lock_controls")).value_or(false);
|
||||||
|
m_IgnoreCollision = !GeneralUtils::TryParse<bool>(m_Parent->GetVarAsString(u"ignore_collision")).value_or(true);
|
||||||
|
m_StickLanding = GeneralUtils::TryParse<bool>(m_Parent->GetVarAsString(u"stickLanding")).value_or(false);
|
||||||
|
m_UsesGroupName = GeneralUtils::TryParse<bool>(m_Parent->GetVarAsString(u"uses_group_name")).value_or(false);
|
||||||
|
m_GroupName = m_Parent->GetVarAsString(u"grp_name");
|
||||||
|
m_MinNumTargets = GeneralUtils::TryParse<int32_t>(m_Parent->GetVarAsString(u"num_targets_to_activate")).value_or(1);
|
||||||
|
m_CinematicPath = m_Parent->GetVarAsString(u"attached_cinematic_path");
|
||||||
|
|
||||||
if (parent->GetLOT() == 7625) {
|
if (parent->GetLOT() == 7625) {
|
||||||
LookupPetSwitch();
|
LookupPetSwitch();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
using namespace GameMessages;
|
||||||
|
RegisterMsg<GetObjectReportInfo>(this, &BouncerComponent::MsgGetObjectReportInfo);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
BouncerComponent::~BouncerComponent() {
|
BouncerComponent::~BouncerComponent() {
|
||||||
@@ -94,3 +112,54 @@ void BouncerComponent::LookupPetSwitch() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool BouncerComponent::MsgGetObjectReportInfo(GameMessages::GameMsg& msg) {
|
||||||
|
auto& reportMsg = static_cast<GameMessages::GetObjectReportInfo&>(msg);
|
||||||
|
auto& cmptType = reportMsg.info->PushDebug("Bouncer");
|
||||||
|
cmptType.PushDebug<AMFIntValue>("Component ID") = GetComponentID();
|
||||||
|
auto& destPos = cmptType.PushDebug("Destination Position");
|
||||||
|
if (m_Destination != NiPoint3Constant::ZERO) {
|
||||||
|
destPos.PushDebug(m_Destination);
|
||||||
|
} else {
|
||||||
|
destPos.PushDebug("<font color=\'#FF0000\'>WARNING:</font> Bouncer has no target position, is likely missing config data");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (m_Speed == -1.0f) {
|
||||||
|
cmptType.PushDebug("<font color=\'#FF0000\'>WARNING:</font> Bouncer has no speed value, is likely missing config data");
|
||||||
|
} else {
|
||||||
|
cmptType.PushDebug<AMFDoubleValue>("Bounce Speed") = m_Speed;
|
||||||
|
}
|
||||||
|
cmptType.PushDebug<AMFStringValue>("Bounce trajectory arc") = m_UsesHighArc ? "High Arc" : "Low Arc";
|
||||||
|
cmptType.PushDebug<AMFBoolValue>("Collision Enabled") = m_IgnoreCollision;
|
||||||
|
cmptType.PushDebug<AMFBoolValue>("Stick Landing") = m_StickLanding;
|
||||||
|
cmptType.PushDebug<AMFBoolValue>("Locks character's controls") = m_LockControls;
|
||||||
|
if (!m_CinematicPath.empty()) cmptType.PushDebug<AMFStringValue>("Cinematic Camera Path (plays during bounce)") = m_CinematicPath;
|
||||||
|
|
||||||
|
auto* switchComponent = m_Parent->GetComponent<SwitchComponent>();
|
||||||
|
auto& respondsToFactions = cmptType.PushDebug("Responds to Factions");
|
||||||
|
if (!switchComponent || switchComponent->GetFactionsToRespondTo().empty()) respondsToFactions.PushDebug("Faction 1");
|
||||||
|
else {
|
||||||
|
for (const auto faction : switchComponent->GetFactionsToRespondTo()) {
|
||||||
|
respondsToFactions.PushDebug(("Faction " + std::to_string(faction)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cmptType.PushDebug<AMFBoolValue>("Uses a group name for interactions") = m_UsesGroupName;
|
||||||
|
if (!m_UsesGroupName) {
|
||||||
|
if (m_MinNumTargets > 1) {
|
||||||
|
cmptType.PushDebug("<font color=\'#FF0000\'>WARNING:</font> Bouncer has a required number of objects to activate, but no group for interactions.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!m_GroupName.empty()) {
|
||||||
|
cmptType.PushDebug("<font color=\'#FF0000\'>WARNING:</font> Has a group name for interactions , but is marked to not use that name.");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (m_GroupName.empty()) {
|
||||||
|
cmptType.PushDebug("<font color=\'#FF0000\'>WARNING:</font> Set to use a group name for inter actions, but no group name is assigned");
|
||||||
|
}
|
||||||
|
cmptType.PushDebug<AMFIntValue>("Number of interactions to activate bouncer") = m_MinNumTargets;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|||||||
@@ -51,6 +51,8 @@ public:
|
|||||||
*/
|
*/
|
||||||
void LookupPetSwitch();
|
void LookupPetSwitch();
|
||||||
|
|
||||||
|
bool MsgGetObjectReportInfo(GameMessages::GameMsg& msg);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/**
|
/**
|
||||||
* Whether this bouncer needs to be activated by a pet
|
* Whether this bouncer needs to be activated by a pet
|
||||||
@@ -66,6 +68,36 @@ private:
|
|||||||
* Whether the pet switch for this bouncer has been located
|
* Whether the pet switch for this bouncer has been located
|
||||||
*/
|
*/
|
||||||
bool m_PetSwitchLoaded;
|
bool m_PetSwitchLoaded;
|
||||||
|
|
||||||
|
// The bouncer destination
|
||||||
|
NiPoint3 m_Destination;
|
||||||
|
|
||||||
|
// The speed at which the player is bounced
|
||||||
|
float m_Speed{};
|
||||||
|
|
||||||
|
// Whether to use a high arc for the bounce trajectory
|
||||||
|
bool m_UsesHighArc{};
|
||||||
|
|
||||||
|
// Lock controls when bouncing
|
||||||
|
bool m_LockControls{};
|
||||||
|
|
||||||
|
// Ignore collision when bouncing
|
||||||
|
bool m_IgnoreCollision{};
|
||||||
|
|
||||||
|
// Stick the landing afterwards or let the player slide
|
||||||
|
bool m_StickLanding{};
|
||||||
|
|
||||||
|
// Whether or not there is a group name
|
||||||
|
bool m_UsesGroupName{};
|
||||||
|
|
||||||
|
// The group name for targets
|
||||||
|
std::string m_GroupName{};
|
||||||
|
|
||||||
|
// The number of targets to activate the bouncer
|
||||||
|
int32_t m_MinNumTargets{};
|
||||||
|
|
||||||
|
// The cinematic path to play during the bounce
|
||||||
|
std::string m_CinematicPath{};
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // BOUNCERCOMPONENT_H
|
#endif // BOUNCERCOMPONENT_H
|
||||||
|
|||||||
@@ -1,5 +1,39 @@
|
|||||||
#include "CollectibleComponent.h"
|
#include "CollectibleComponent.h"
|
||||||
|
|
||||||
|
#include "MissionComponent.h"
|
||||||
|
#include "dServer.h"
|
||||||
|
#include "Amf3.h"
|
||||||
|
|
||||||
|
CollectibleComponent::CollectibleComponent(Entity* parentEntity, const int32_t componentID, const int32_t collectibleId) :
|
||||||
|
Component(parentEntity, componentID), m_CollectibleId(collectibleId) {
|
||||||
|
using namespace GameMessages;
|
||||||
|
RegisterMsg<GetObjectReportInfo>(this, &CollectibleComponent::MsgGetObjectReportInfo);
|
||||||
|
}
|
||||||
|
|
||||||
void CollectibleComponent::Serialize(RakNet::BitStream& outBitStream, bool isConstruction) {
|
void CollectibleComponent::Serialize(RakNet::BitStream& outBitStream, bool isConstruction) {
|
||||||
outBitStream.Write(GetCollectibleId());
|
outBitStream.Write(GetCollectibleId());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool CollectibleComponent::MsgGetObjectReportInfo(GameMessages::GameMsg& msg) {
|
||||||
|
auto& reportMsg = static_cast<GameMessages::GetObjectReportInfo&>(msg);
|
||||||
|
auto& cmptType = reportMsg.info->PushDebug("Collectible");
|
||||||
|
auto collectibleID = static_cast<uint32_t>(m_CollectibleId) + static_cast<uint32_t>(Game::server->GetZoneID() << 8);
|
||||||
|
|
||||||
|
cmptType.PushDebug<AMFIntValue>("Component ID") = GetComponentID();
|
||||||
|
|
||||||
|
cmptType.PushDebug<AMFIntValue>("Collectible ID") = GetCollectibleId();
|
||||||
|
cmptType.PushDebug<AMFIntValue>("Mission Tracking ID (for save data)") = collectibleID;
|
||||||
|
|
||||||
|
auto* localCharEntity = Game::entityManager->GetEntity(reportMsg.clientID);
|
||||||
|
bool collected = false;
|
||||||
|
if (localCharEntity) {
|
||||||
|
auto* missionComponent = localCharEntity->GetComponent<MissionComponent>();
|
||||||
|
|
||||||
|
if (m_CollectibleId != 0) {
|
||||||
|
collected = missionComponent->HasCollectible(collectibleID);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cmptType.PushDebug<AMFBoolValue>("Has been collected") = collected;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|||||||
@@ -7,10 +7,12 @@
|
|||||||
class CollectibleComponent final : public Component {
|
class CollectibleComponent final : public Component {
|
||||||
public:
|
public:
|
||||||
static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::COLLECTIBLE;
|
static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::COLLECTIBLE;
|
||||||
CollectibleComponent(Entity* parentEntity, const int32_t componentID, const int32_t collectibleId) : Component(parentEntity, componentID), m_CollectibleId(collectibleId) {}
|
CollectibleComponent(Entity* parentEntity, const int32_t componentID, const int32_t collectibleId);
|
||||||
|
|
||||||
int16_t GetCollectibleId() const { return m_CollectibleId; }
|
int16_t GetCollectibleId() const { return m_CollectibleId; }
|
||||||
void Serialize(RakNet::BitStream& outBitStream, bool isConstruction) override;
|
void Serialize(RakNet::BitStream& outBitStream, bool isConstruction) override;
|
||||||
|
|
||||||
|
bool MsgGetObjectReportInfo(GameMessages::GameMsg& msg);
|
||||||
private:
|
private:
|
||||||
int16_t m_CollectibleId = 0;
|
int16_t m_CollectibleId = 0;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -3,6 +3,9 @@
|
|||||||
#include "Logger.h"
|
#include "Logger.h"
|
||||||
#include "Game.h"
|
#include "Game.h"
|
||||||
#include "dConfig.h"
|
#include "dConfig.h"
|
||||||
|
#include "CDLootMatrixTable.h"
|
||||||
|
#include "CDLootTableTable.h"
|
||||||
|
#include "CDRarityTableTable.h"
|
||||||
|
|
||||||
#include "Amf3.h"
|
#include "Amf3.h"
|
||||||
#include "AmfSerialize.h"
|
#include "AmfSerialize.h"
|
||||||
@@ -982,7 +985,14 @@ void DestroyableComponent::DoHardcoreModeDrops(const LWOOBJID source) {
|
|||||||
for (const auto item : itemMap | std::views::values) {
|
for (const auto item : itemMap | std::views::values) {
|
||||||
// Don't drop excluded items or null ones
|
// Don't drop excluded items or null ones
|
||||||
if (!item || Game::entityManager->GetHardcoreExcludedItemDrops().contains(item->GetLot())) continue;
|
if (!item || Game::entityManager->GetHardcoreExcludedItemDrops().contains(item->GetLot())) continue;
|
||||||
GameMessages::SendDropClientLoot(m_Parent, source, item->GetLot(), 0, m_Parent->GetPosition(), item->GetCount());
|
GameMessages::DropClientLoot lootMsg{};
|
||||||
|
lootMsg.target = m_Parent->GetObjectID();
|
||||||
|
lootMsg.ownerID = m_Parent->GetObjectID();
|
||||||
|
lootMsg.sourceID = m_Parent->GetObjectID();
|
||||||
|
lootMsg.item = item->GetLot();
|
||||||
|
lootMsg.count = 1;
|
||||||
|
lootMsg.spawnPos = m_Parent->GetPosition();
|
||||||
|
for (int i = 0; i < item->GetCount(); i++) Loot::DropItem(*m_Parent, lootMsg);
|
||||||
item->SetCount(0, false, false);
|
item->SetCount(0, false, false);
|
||||||
}
|
}
|
||||||
Game::entityManager->SerializeEntity(m_Parent);
|
Game::entityManager->SerializeEntity(m_Parent);
|
||||||
@@ -1005,12 +1015,24 @@ void DestroyableComponent::DoHardcoreModeDrops(const LWOOBJID source) {
|
|||||||
|
|
||||||
//drop all coins:
|
//drop all coins:
|
||||||
constexpr auto MAX_TO_DROP_PER_GM = 100'000;
|
constexpr auto MAX_TO_DROP_PER_GM = 100'000;
|
||||||
|
GameMessages::DropClientLoot lootMsg{};
|
||||||
|
lootMsg.target = m_Parent->GetObjectID();
|
||||||
|
lootMsg.ownerID = m_Parent->GetObjectID();
|
||||||
|
lootMsg.spawnPos = m_Parent->GetPosition();
|
||||||
|
lootMsg.sourceID = source;
|
||||||
|
lootMsg.item = LOT_NULL;
|
||||||
|
lootMsg.Send();
|
||||||
|
lootMsg.Send(m_Parent->GetSystemAddress());
|
||||||
while (coinsToDrop > MAX_TO_DROP_PER_GM) {
|
while (coinsToDrop > MAX_TO_DROP_PER_GM) {
|
||||||
LOG("Dropping 100,000, %llu left", coinsToDrop);
|
LOG("Dropping 100,000, %llu left", coinsToDrop);
|
||||||
GameMessages::SendDropClientLoot(m_Parent, source, LOT_NULL, MAX_TO_DROP_PER_GM, m_Parent->GetPosition());
|
lootMsg.currency = 100'000;
|
||||||
coinsToDrop -= MAX_TO_DROP_PER_GM;
|
lootMsg.Send();
|
||||||
|
lootMsg.Send(m_Parent->GetSystemAddress());
|
||||||
|
coinsToDrop -= 100'000;
|
||||||
}
|
}
|
||||||
GameMessages::SendDropClientLoot(m_Parent, source, LOT_NULL, coinsToDrop, m_Parent->GetPosition());
|
lootMsg.currency = coinsToDrop;
|
||||||
|
lootMsg.Send();
|
||||||
|
lootMsg.Send(m_Parent->GetSystemAddress());
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -1041,38 +1063,89 @@ void DestroyableComponent::DoHardcoreModeDrops(const LWOOBJID source) {
|
|||||||
|
|
||||||
bool DestroyableComponent::OnGetObjectReportInfo(GameMessages::GameMsg& msg) {
|
bool DestroyableComponent::OnGetObjectReportInfo(GameMessages::GameMsg& msg) {
|
||||||
auto& reportInfo = static_cast<GameMessages::GetObjectReportInfo&>(msg);
|
auto& reportInfo = static_cast<GameMessages::GetObjectReportInfo&>(msg);
|
||||||
|
|
||||||
auto& destroyableInfo = reportInfo.info->PushDebug("Destroyable");
|
auto& destroyableInfo = reportInfo.info->PushDebug("Destroyable");
|
||||||
destroyableInfo.PushDebug<AMFIntValue>("Health") = m_iHealth;
|
destroyableInfo.PushDebug<AMFIntValue>("DestructibleComponent DB Table Template ID") = m_ComponentID;
|
||||||
destroyableInfo.PushDebug<AMFDoubleValue>("Max Health") = m_fMaxHealth;
|
|
||||||
destroyableInfo.PushDebug<AMFIntValue>("Armor") = m_iArmor;
|
if (m_CurrencyIndex == -1) {
|
||||||
destroyableInfo.PushDebug<AMFDoubleValue>("Max Armor") = m_fMaxArmor;
|
destroyableInfo.PushDebug<AMFBoolValue>("Has Loot Currency") = false;
|
||||||
destroyableInfo.PushDebug<AMFIntValue>("Imagination") = m_iImagination;
|
} else {
|
||||||
destroyableInfo.PushDebug<AMFDoubleValue>("Max Imagination") = m_fMaxImagination;
|
destroyableInfo.PushDebug<AMFIntValue>("Loot Currency ID") = m_CurrencyIndex;
|
||||||
destroyableInfo.PushDebug<AMFIntValue>("Damage To Absorb") = m_DamageToAbsorb;
|
auto& detailedCoinInfo = destroyableInfo.PushDebug("Coin Info");
|
||||||
destroyableInfo.PushDebug<AMFBoolValue>("Is GM Immune") = m_IsGMImmune;
|
detailedCoinInfo.PushDebug<AMFIntValue>("Min Coins") = m_MinCoins;
|
||||||
destroyableInfo.PushDebug<AMFBoolValue>("Is Shielded") = m_IsShielded;
|
detailedCoinInfo.PushDebug<AMFIntValue>("Max Coins") = m_MaxCoins;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_LootMatrixID == -1 || m_LootMatrixID == 0) {
|
||||||
|
destroyableInfo.PushDebug<AMFBoolValue>("Has Loot Matrix") = false;
|
||||||
|
} else {
|
||||||
|
auto& lootInfo = destroyableInfo.PushDebug("Loot Info");
|
||||||
|
lootInfo.PushDebug<AMFIntValue>("Loot Matrix ID") = m_LootMatrixID;
|
||||||
|
auto* const componentsRegistryTable = CDClientManager::GetTable<CDComponentsRegistryTable>();
|
||||||
|
auto* const itemComponentTable = CDClientManager::GetTable<CDItemComponentTable>();
|
||||||
|
auto* const lootMatrixTable = CDClientManager::GetTable<CDLootMatrixTable>();
|
||||||
|
auto* const lootTableTable = CDClientManager::GetTable<CDLootTableTable>();
|
||||||
|
auto* const rarityTableTable = CDClientManager::GetTable<CDRarityTableTable>();
|
||||||
|
|
||||||
|
const auto& matrix = lootMatrixTable->GetMatrix(m_LootMatrixID);
|
||||||
|
|
||||||
|
for (const auto& entry : matrix) {
|
||||||
|
auto& thisEntry = lootInfo.PushDebug("Loot table Index - " + std::to_string(entry.LootTableIndex));
|
||||||
|
thisEntry.PushDebug<AMFDoubleValue>("Percent chance to drop") = entry.percent * 100.0f;
|
||||||
|
thisEntry.PushDebug<AMFDoubleValue>("Minimum amount to drop") = entry.minToDrop;
|
||||||
|
thisEntry.PushDebug<AMFDoubleValue>("Maximum amount to drop") = entry.maxToDrop;
|
||||||
|
const auto& lootTable = lootTableTable->GetTable(entry.LootTableIndex);
|
||||||
|
const auto& rarityTable = rarityTableTable->GetRarityTable(entry.RarityTableIndex);
|
||||||
|
|
||||||
|
auto& thisRarity = thisEntry.PushDebug("Rarity");
|
||||||
|
for (const auto& rarity : rarityTable) {
|
||||||
|
thisRarity.PushDebug<AMFDoubleValue>("Rarity " + std::to_string(rarity.rarity)) = rarity.randmax;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto& thisItems = thisEntry.PushDebug("Drop(s) Info");
|
||||||
|
for (const auto& loot : lootTable) {
|
||||||
|
uint32_t itemComponentId = componentsRegistryTable->GetByIDAndType(loot.itemid, eReplicaComponentType::ITEM);
|
||||||
|
uint32_t rarity = itemComponentTable->GetItemComponentByID(itemComponentId).rarity;
|
||||||
|
auto title = "%[Objects_" + std::to_string(loot.itemid) + "_name] " + std::to_string(loot.itemid);
|
||||||
|
if (loot.MissionDrop) title += " - Mission Drop";
|
||||||
|
thisItems.PushDebug(title);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto* const entity = Game::entityManager->GetEntity(reportInfo.clientID);
|
||||||
|
destroyableInfo.PushDebug<AMFBoolValue>("Is on your team") = entity ? IsFriend(entity) : false;
|
||||||
|
auto& stats = destroyableInfo.PushDebug("Statistics");
|
||||||
|
stats.PushDebug<AMFIntValue>("Health") = m_iHealth;
|
||||||
|
stats.PushDebug<AMFDoubleValue>("Maximum Health") = m_fMaxHealth;
|
||||||
|
stats.PushDebug<AMFIntValue>("Armor") = m_iArmor;
|
||||||
|
stats.PushDebug<AMFDoubleValue>("Maximum Armor") = m_fMaxArmor;
|
||||||
|
stats.PushDebug<AMFIntValue>("Imagination") = m_iImagination;
|
||||||
|
stats.PushDebug<AMFDoubleValue>("Maximum Imagination") = m_fMaxImagination;
|
||||||
|
stats.PushDebug<AMFIntValue>("Damage Absorption Points") = m_DamageToAbsorb;
|
||||||
|
stats.PushDebug<AMFBoolValue>("Is GM Immune") = m_IsGMImmune;
|
||||||
|
stats.PushDebug<AMFBoolValue>("Is Shielded") = m_IsShielded;
|
||||||
destroyableInfo.PushDebug<AMFIntValue>("Attacks To Block") = m_AttacksToBlock;
|
destroyableInfo.PushDebug<AMFIntValue>("Attacks To Block") = m_AttacksToBlock;
|
||||||
destroyableInfo.PushDebug<AMFIntValue>("Damage Reduction") = m_DamageReduction;
|
destroyableInfo.PushDebug<AMFIntValue>("Damage Reduction") = m_DamageReduction;
|
||||||
auto& factions = destroyableInfo.PushDebug("Factions");
|
std::stringstream factionsStream;
|
||||||
size_t i = 0;
|
|
||||||
for (const auto factionID : m_FactionIDs) {
|
for (const auto factionID : m_FactionIDs) {
|
||||||
factions.PushDebug<AMFStringValue>(std::to_string(i++) + " " + std::to_string(factionID)) = "";
|
factionsStream << factionID << " ";
|
||||||
}
|
}
|
||||||
auto& enemyFactions = destroyableInfo.PushDebug("Enemy Factions");
|
|
||||||
i = 0;
|
destroyableInfo.PushDebug<AMFStringValue>("Factions") = factionsStream.str();
|
||||||
|
|
||||||
|
factionsStream.str("");
|
||||||
for (const auto enemyFactionID : m_EnemyFactionIDs) {
|
for (const auto enemyFactionID : m_EnemyFactionIDs) {
|
||||||
enemyFactions.PushDebug<AMFStringValue>(std::to_string(i++) + " " + std::to_string(enemyFactionID)) = "";
|
factionsStream << enemyFactionID << " ";
|
||||||
}
|
}
|
||||||
destroyableInfo.PushDebug<AMFBoolValue>("Is Smashable") = m_IsSmashable;
|
|
||||||
destroyableInfo.PushDebug<AMFBoolValue>("Is Dead") = m_IsDead;
|
destroyableInfo.PushDebug<AMFStringValue>("Enemy Factions") = factionsStream.str();
|
||||||
|
|
||||||
|
destroyableInfo.PushDebug<AMFBoolValue>("Is A Smashable") = m_IsSmashable;
|
||||||
destroyableInfo.PushDebug<AMFBoolValue>("Is Smashed") = m_IsSmashed;
|
destroyableInfo.PushDebug<AMFBoolValue>("Is Smashed") = m_IsSmashed;
|
||||||
destroyableInfo.PushDebug<AMFBoolValue>("Is Module Assembly") = m_IsModuleAssembly;
|
destroyableInfo.PushDebug<AMFBoolValue>("Is Module Assembly") = m_IsModuleAssembly;
|
||||||
destroyableInfo.PushDebug<AMFDoubleValue>("Explode Factor") = m_ExplodeFactor;
|
destroyableInfo.PushDebug<AMFDoubleValue>("Explode Factor") = m_ExplodeFactor;
|
||||||
destroyableInfo.PushDebug<AMFBoolValue>("Has Threats") = m_HasThreats;
|
destroyableInfo.PushDebug<AMFBoolValue>("Has Threats") = m_HasThreats;
|
||||||
destroyableInfo.PushDebug<AMFIntValue>("Loot Matrix ID") = m_LootMatrixID;
|
|
||||||
destroyableInfo.PushDebug<AMFIntValue>("Min Coins") = m_MinCoins;
|
|
||||||
destroyableInfo.PushDebug<AMFIntValue>("Max Coins") = m_MaxCoins;
|
|
||||||
destroyableInfo.PushDebug<AMFStringValue>("Killer ID") = std::to_string(m_KillerID);
|
destroyableInfo.PushDebug<AMFStringValue>("Killer ID") = std::to_string(m_KillerID);
|
||||||
|
|
||||||
// "Scripts"; idk what to do about scripts yet
|
// "Scripts"; idk what to do about scripts yet
|
||||||
@@ -1087,7 +1160,25 @@ bool DestroyableComponent::OnGetObjectReportInfo(GameMessages::GameMsg& msg) {
|
|||||||
immuneCounts.PushDebug<AMFIntValue>("Quickbuild Interrupt") = m_ImmuneToQuickbuildInterruptCount;
|
immuneCounts.PushDebug<AMFIntValue>("Quickbuild Interrupt") = m_ImmuneToQuickbuildInterruptCount;
|
||||||
immuneCounts.PushDebug<AMFIntValue>("Pull To Point") = m_ImmuneToPullToPointCount;
|
immuneCounts.PushDebug<AMFIntValue>("Pull To Point") = m_ImmuneToPullToPointCount;
|
||||||
|
|
||||||
destroyableInfo.PushDebug<AMFIntValue>("Death Behavior") = m_DeathBehavior;
|
auto& deathInfo = destroyableInfo.PushDebug("Death Info");
|
||||||
|
deathInfo.PushDebug<AMFBoolValue>("Is Dead") = m_IsDead;
|
||||||
|
switch (m_DeathBehavior) {
|
||||||
|
case 0:
|
||||||
|
deathInfo.PushDebug<AMFStringValue>("Death Behavior") = "Fade";
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
deathInfo.PushDebug<AMFStringValue>("Death Behavior") = "Stay";
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
deathInfo.PushDebug<AMFStringValue>("Death Behavior") = "Immediate";
|
||||||
|
break;
|
||||||
|
case -1:
|
||||||
|
deathInfo.PushDebug<AMFStringValue>("Death Behavior") = "Invulnerable";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
deathInfo.PushDebug<AMFStringValue>("Death Behavior") = "Other";
|
||||||
|
break;
|
||||||
|
}
|
||||||
destroyableInfo.PushDebug<AMFDoubleValue>("Damage Cooldown Timer") = m_DamageCooldownTimer;
|
destroyableInfo.PushDebug<AMFDoubleValue>("Damage Cooldown Timer") = m_DamageCooldownTimer;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -370,6 +370,8 @@ public:
|
|||||||
*/
|
*/
|
||||||
uint32_t GetLootMatrixID() const { return m_LootMatrixID; }
|
uint32_t GetLootMatrixID() const { return m_LootMatrixID; }
|
||||||
|
|
||||||
|
void SetCurrencyIndex(int32_t currencyIndex) { m_CurrencyIndex = currencyIndex; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the ID of the entity that killed this entity, if any
|
* Returns the ID of the entity that killed this entity, if any
|
||||||
* @return the ID of the entity that killed this entity, if any
|
* @return the ID of the entity that killed this entity, if any
|
||||||
@@ -587,6 +589,9 @@ private:
|
|||||||
*/
|
*/
|
||||||
uint32_t m_LootMatrixID;
|
uint32_t m_LootMatrixID;
|
||||||
|
|
||||||
|
// The currency index to determine how much loot to drop
|
||||||
|
int32_t m_CurrencyIndex{ -1 };
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The min amount of coins that will drop when this entity is smashed
|
* The min amount of coins that will drop when this entity is smashed
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,9 +1,14 @@
|
|||||||
#include "GhostComponent.h"
|
#include "GhostComponent.h"
|
||||||
|
|
||||||
|
#include "Amf3.h"
|
||||||
|
#include "GameMessages.h"
|
||||||
|
|
||||||
GhostComponent::GhostComponent(Entity* parent, const int32_t componentID) : Component(parent, componentID) {
|
GhostComponent::GhostComponent(Entity* parent, const int32_t componentID) : Component(parent, componentID) {
|
||||||
m_GhostReferencePoint = NiPoint3Constant::ZERO;
|
m_GhostReferencePoint = NiPoint3Constant::ZERO;
|
||||||
m_GhostOverridePoint = NiPoint3Constant::ZERO;
|
m_GhostOverridePoint = NiPoint3Constant::ZERO;
|
||||||
m_GhostOverride = false;
|
m_GhostOverride = false;
|
||||||
|
|
||||||
|
RegisterMsg<GameMessages::GetObjectReportInfo>(this, &GhostComponent::MsgGetObjectReportInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
GhostComponent::~GhostComponent() {
|
GhostComponent::~GhostComponent() {
|
||||||
@@ -55,3 +60,12 @@ bool GhostComponent::IsObserved(LWOOBJID id) {
|
|||||||
void GhostComponent::GhostEntity(LWOOBJID id) {
|
void GhostComponent::GhostEntity(LWOOBJID id) {
|
||||||
m_ObservedEntities.erase(id);
|
m_ObservedEntities.erase(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool GhostComponent::MsgGetObjectReportInfo(GameMessages::GameMsg& msg) {
|
||||||
|
auto& reportMsg = static_cast<GameMessages::GetObjectReportInfo&>(msg);
|
||||||
|
auto& cmptType = reportMsg.info->PushDebug("Ghost");
|
||||||
|
cmptType.PushDebug<AMFIntValue>("Component ID") = GetComponentID();
|
||||||
|
cmptType.PushDebug<AMFBoolValue>("Is GM Invis") = false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|||||||
@@ -39,6 +39,8 @@ public:
|
|||||||
|
|
||||||
void GhostEntity(const LWOOBJID id);
|
void GhostEntity(const LWOOBJID id);
|
||||||
|
|
||||||
|
bool MsgGetObjectReportInfo(GameMessages::GameMsg& msg);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
NiPoint3 m_GhostReferencePoint;
|
NiPoint3 m_GhostReferencePoint;
|
||||||
|
|
||||||
|
|||||||
@@ -282,7 +282,14 @@ void InventoryComponent::AddItem(
|
|||||||
|
|
||||||
case 1:
|
case 1:
|
||||||
for (size_t i = 0; i < size; i++) {
|
for (size_t i = 0; i < size; i++) {
|
||||||
GameMessages::SendDropClientLoot(this->m_Parent, this->m_Parent->GetObjectID(), lot, 0, this->m_Parent->GetPosition(), 1);
|
GameMessages::DropClientLoot lootMsg{};
|
||||||
|
lootMsg.target = m_Parent->GetObjectID();
|
||||||
|
lootMsg.ownerID = m_Parent->GetObjectID();
|
||||||
|
lootMsg.sourceID = m_Parent->GetObjectID();
|
||||||
|
lootMsg.item = lot;
|
||||||
|
lootMsg.count = 1;
|
||||||
|
lootMsg.spawnPos = m_Parent->GetPosition();
|
||||||
|
Loot::DropItem(*m_Parent, lootMsg);
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ ModelComponent::ModelComponent(Entity* parent, const int32_t componentID) : Comp
|
|||||||
using namespace GameMessages;
|
using namespace GameMessages;
|
||||||
m_OriginalPosition = m_Parent->GetDefaultPosition();
|
m_OriginalPosition = m_Parent->GetDefaultPosition();
|
||||||
m_OriginalRotation = m_Parent->GetDefaultRotation();
|
m_OriginalRotation = m_Parent->GetDefaultRotation();
|
||||||
|
LOG("%f %f %f %f", m_OriginalRotation.x, m_OriginalRotation.y, m_OriginalRotation.z, m_OriginalRotation.w);
|
||||||
m_IsPaused = false;
|
m_IsPaused = false;
|
||||||
m_NumListeningInteract = 0;
|
m_NumListeningInteract = 0;
|
||||||
|
|
||||||
@@ -38,6 +39,10 @@ bool ModelComponent::OnResetModelToDefaults(GameMessages::GameMsg& msg) {
|
|||||||
m_Parent->SetPosition(m_OriginalPosition);
|
m_Parent->SetPosition(m_OriginalPosition);
|
||||||
m_Parent->SetRotation(m_OriginalRotation);
|
m_Parent->SetRotation(m_OriginalRotation);
|
||||||
m_Parent->SetVelocity(NiPoint3Constant::ZERO);
|
m_Parent->SetVelocity(NiPoint3Constant::ZERO);
|
||||||
|
GameMessages::SetAngularVelocity setAngVel;
|
||||||
|
setAngVel.target = m_Parent->GetObjectID();
|
||||||
|
setAngVel.angVelocity = NiPoint3Constant::ZERO;
|
||||||
|
setAngVel.Send();
|
||||||
|
|
||||||
m_Speed = 3.0f;
|
m_Speed = 3.0f;
|
||||||
m_NumListeningInteract = 0;
|
m_NumListeningInteract = 0;
|
||||||
@@ -304,6 +309,38 @@ void ModelComponent::SetVelocity(const NiPoint3& velocity) const {
|
|||||||
m_Parent->SetVelocity(velocity);
|
m_Parent->SetVelocity(velocity);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ModelComponent::TrySetAngularVelocity(const NiPoint3& angularVelocity) const {
|
||||||
|
GameMessages::GetAngularVelocity getAngVel{};
|
||||||
|
getAngVel.target = m_Parent->GetObjectID();
|
||||||
|
if (!getAngVel.Send()) {
|
||||||
|
LOG("Couldn't get angular velocity for %llu", m_Parent->GetObjectID());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
GameMessages::SetAngularVelocity setAngVel{};
|
||||||
|
setAngVel.target = m_Parent->GetObjectID();
|
||||||
|
if (angularVelocity != NiPoint3Constant::ZERO) {
|
||||||
|
setAngVel.angVelocity = getAngVel.angVelocity;
|
||||||
|
const auto [x, y, z] = angularVelocity * m_Speed;
|
||||||
|
if (x != 0.0f) {
|
||||||
|
if (getAngVel.angVelocity.x != 0.0f) return false;
|
||||||
|
setAngVel.angVelocity.x = x;
|
||||||
|
} else if (y != 0.0f) {
|
||||||
|
if (getAngVel.angVelocity.y != 0.0f) return false;
|
||||||
|
setAngVel.angVelocity.y = y;
|
||||||
|
} else if (z != 0.0f) {
|
||||||
|
if (getAngVel.angVelocity.z != 0.0f) return false;
|
||||||
|
setAngVel.angVelocity.z = z;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
setAngVel.angVelocity = angularVelocity;
|
||||||
|
}
|
||||||
|
LOG("Setting angular velocity to %f %f %f", setAngVel.angVelocity.x, setAngVel.angVelocity.y, setAngVel.angVelocity.z);
|
||||||
|
setAngVel.Send();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
void ModelComponent::OnChatMessageReceived(const std::string& sMessage) {
|
void ModelComponent::OnChatMessageReceived(const std::string& sMessage) {
|
||||||
for (auto& behavior : m_Behaviors) behavior.OnChatMessageReceived(sMessage);
|
for (auto& behavior : m_Behaviors) behavior.OnChatMessageReceived(sMessage);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -145,6 +145,11 @@ public:
|
|||||||
// Force sets the velocity to a value.
|
// Force sets the velocity to a value.
|
||||||
void SetVelocity(const NiPoint3& velocity) const;
|
void SetVelocity(const NiPoint3& velocity) const;
|
||||||
|
|
||||||
|
// Attempts to set the angular velocity of the model.
|
||||||
|
// If the axis currently has a velocity of zero, returns true.
|
||||||
|
// If the axis is currently controlled by a behavior, returns false.
|
||||||
|
bool TrySetAngularVelocity(const NiPoint3& angularVelocity) const;
|
||||||
|
|
||||||
void OnChatMessageReceived(const std::string& sMessage);
|
void OnChatMessageReceived(const std::string& sMessage);
|
||||||
|
|
||||||
void OnHit();
|
void OnHit();
|
||||||
@@ -162,6 +167,8 @@ public:
|
|||||||
// Decrements the number of strips listening for an attack.
|
// Decrements the number of strips listening for an attack.
|
||||||
// If this is the last strip removing an attack, it will reset the factions to the default of -1.
|
// If this is the last strip removing an attack, it will reset the factions to the default of -1.
|
||||||
void RemoveAttack();
|
void RemoveAttack();
|
||||||
|
|
||||||
|
float GetSpeed() const noexcept { return m_Speed; }
|
||||||
private:
|
private:
|
||||||
|
|
||||||
// Loads a behavior from the database.
|
// Loads a behavior from the database.
|
||||||
|
|||||||
@@ -15,8 +15,11 @@
|
|||||||
#include "StringifiedEnum.h"
|
#include "StringifiedEnum.h"
|
||||||
#include "Amf3.h"
|
#include "Amf3.h"
|
||||||
|
|
||||||
SimplePhysicsComponent::SimplePhysicsComponent(Entity* parent, const int32_t componentID) : PhysicsComponent(parent, componentID) {
|
SimplePhysicsComponent::SimplePhysicsComponent(Entity* parent, int32_t componentID) : PhysicsComponent(parent, componentID) {
|
||||||
RegisterMsg(MessageType::Game::GET_OBJECT_REPORT_INFO, this, &SimplePhysicsComponent::OnGetObjectReportInfo);
|
using namespace GameMessages;
|
||||||
|
RegisterMsg<GetObjectReportInfo>(this, &SimplePhysicsComponent::OnGetObjectReportInfo);
|
||||||
|
RegisterMsg<GameMessages::GetAngularVelocity>(this, &SimplePhysicsComponent::OnGetAngularVelocity);
|
||||||
|
RegisterMsg<GameMessages::SetAngularVelocity>(this, &SimplePhysicsComponent::OnSetAngularVelocity);
|
||||||
|
|
||||||
m_Position = m_Parent->GetDefaultPosition();
|
m_Position = m_Parent->GetDefaultPosition();
|
||||||
m_Rotation = m_Parent->GetDefaultRotation();
|
m_Rotation = m_Parent->GetDefaultRotation();
|
||||||
@@ -38,10 +41,20 @@ SimplePhysicsComponent::~SimplePhysicsComponent() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void SimplePhysicsComponent::Update(const float deltaTime) {
|
void SimplePhysicsComponent::Update(const float deltaTime) {
|
||||||
if (m_Velocity == NiPoint3Constant::ZERO) return;
|
if (m_Velocity != NiPoint3Constant::ZERO) {
|
||||||
m_Position += m_Velocity * deltaTime;
|
m_Position += m_Velocity * deltaTime;
|
||||||
m_DirtyPosition = true;
|
m_DirtyPosition = true;
|
||||||
Game::entityManager->SerializeEntity(m_Parent);
|
Game::entityManager->SerializeEntity(m_Parent);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_AngularVelocity != NiPoint3Constant::ZERO) {
|
||||||
|
m_Rotation.Normalize();
|
||||||
|
const auto vel = NiQuaternion::FromEulerAngles(m_AngularVelocity * deltaTime);
|
||||||
|
m_Rotation *= vel;
|
||||||
|
const auto euler = m_Rotation.GetEulerAngles();
|
||||||
|
m_DirtyPosition = true;
|
||||||
|
Game::entityManager->SerializeEntity(m_Parent);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void SimplePhysicsComponent::Serialize(RakNet::BitStream& outBitStream, bool bIsInitialUpdate) {
|
void SimplePhysicsComponent::Serialize(RakNet::BitStream& outBitStream, bool bIsInitialUpdate) {
|
||||||
@@ -52,8 +65,12 @@ void SimplePhysicsComponent::Serialize(RakNet::BitStream& outBitStream, bool bIs
|
|||||||
|
|
||||||
outBitStream.Write(m_DirtyVelocity || bIsInitialUpdate);
|
outBitStream.Write(m_DirtyVelocity || bIsInitialUpdate);
|
||||||
if (m_DirtyVelocity || bIsInitialUpdate) {
|
if (m_DirtyVelocity || bIsInitialUpdate) {
|
||||||
outBitStream.Write(m_Velocity);
|
outBitStream.Write(m_Velocity.x);
|
||||||
outBitStream.Write(m_AngularVelocity);
|
outBitStream.Write(m_Velocity.y);
|
||||||
|
outBitStream.Write(m_Velocity.z);
|
||||||
|
outBitStream.Write(m_AngularVelocity.x);
|
||||||
|
outBitStream.Write(m_AngularVelocity.y);
|
||||||
|
outBitStream.Write(m_AngularVelocity.z);
|
||||||
|
|
||||||
m_DirtyVelocity = false;
|
m_DirtyVelocity = false;
|
||||||
}
|
}
|
||||||
@@ -92,3 +109,18 @@ bool SimplePhysicsComponent::OnGetObjectReportInfo(GameMessages::GameMsg& msg) {
|
|||||||
info.PushDebug<AMFStringValue>("Climbable Type") = StringifiedEnum::ToString(m_ClimbableType).data();
|
info.PushDebug<AMFStringValue>("Climbable Type") = StringifiedEnum::ToString(m_ClimbableType).data();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool SimplePhysicsComponent::OnSetAngularVelocity(GameMessages::GameMsg& msg) {
|
||||||
|
auto& setAngVel = static_cast<GameMessages::SetAngularVelocity&>(msg);
|
||||||
|
m_DirtyVelocity |= setAngVel.bForceFlagDirty || (m_AngularVelocity != setAngVel.angVelocity);
|
||||||
|
m_AngularVelocity = setAngVel.angVelocity;
|
||||||
|
LOG("Velocity is now %f %f %f", m_AngularVelocity.x, m_AngularVelocity.y, m_AngularVelocity.z);
|
||||||
|
Game::entityManager->SerializeEntity(m_Parent);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SimplePhysicsComponent::OnGetAngularVelocity(GameMessages::GameMsg& msg) {
|
||||||
|
auto& getAngVel = static_cast<GameMessages::GetAngularVelocity&>(msg);
|
||||||
|
getAngVel.angVelocity = m_AngularVelocity;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|||||||
@@ -61,6 +61,9 @@ public:
|
|||||||
*/
|
*/
|
||||||
void SetAngularVelocity(const NiPoint3& value) { m_AngularVelocity = value; m_DirtyVelocity = true; }
|
void SetAngularVelocity(const NiPoint3& value) { m_AngularVelocity = value; m_DirtyVelocity = true; }
|
||||||
|
|
||||||
|
bool OnSetAngularVelocity(GameMessages::GameMsg& msg);
|
||||||
|
bool OnGetAngularVelocity(GameMessages::GameMsg& msg);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the physics motion state
|
* Returns the physics motion state
|
||||||
* @return the physics motion state
|
* @return the physics motion state
|
||||||
|
|||||||
@@ -67,6 +67,10 @@ public:
|
|||||||
*/
|
*/
|
||||||
static SwitchComponent* GetClosestSwitch(NiPoint3 position);
|
static SwitchComponent* GetClosestSwitch(NiPoint3 position);
|
||||||
|
|
||||||
|
const std::vector<int32_t>& GetFactionsToRespondTo() const {
|
||||||
|
return m_FactionsToRespondTo;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/**
|
/**
|
||||||
* A list of all pet switches.
|
* A list of all pet switches.
|
||||||
|
|||||||
@@ -1067,44 +1067,6 @@ void GameMessages::SendSetNetworkScriptVar(Entity* entity, const SystemAddress&
|
|||||||
SEND_PACKET;
|
SEND_PACKET;
|
||||||
}
|
}
|
||||||
|
|
||||||
void GameMessages::SendDropClientLoot(Entity* entity, const LWOOBJID& sourceID, LOT item, int currency, NiPoint3 spawnPos, int count) {
|
|
||||||
if (Game::config->GetValue("disable_drops") == "1" || !entity) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool bUsePosition = false;
|
|
||||||
NiPoint3 finalPosition;
|
|
||||||
LWOOBJID lootID = LWOOBJID_EMPTY;
|
|
||||||
LWOOBJID owner = entity->GetObjectID();
|
|
||||||
|
|
||||||
if (item != LOT_NULL && item != 0) {
|
|
||||||
lootID = ObjectIDManager::GenerateObjectID();
|
|
||||||
|
|
||||||
Loot::Info info;
|
|
||||||
info.id = lootID;
|
|
||||||
info.count = count;
|
|
||||||
info.lot = item;
|
|
||||||
entity->AddLootItem(info);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (item == LOT_NULL && currency != 0) {
|
|
||||||
entity->RegisterCoinDrop(currency);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (spawnPos != NiPoint3Constant::ZERO) {
|
|
||||||
bUsePosition = true;
|
|
||||||
|
|
||||||
//Calculate where the loot will go:
|
|
||||||
uint16_t degree = GeneralUtils::GenerateRandomNumber<uint16_t>(0, 360);
|
|
||||||
|
|
||||||
double rad = degree * 3.14 / 180;
|
|
||||||
double sin_v = sin(rad) * 4.2;
|
|
||||||
double cos_v = cos(rad) * 4.2;
|
|
||||||
|
|
||||||
finalPosition = NiPoint3(static_cast<float>(spawnPos.GetX() + sin_v), spawnPos.GetY(), static_cast<float>(spawnPos.GetZ() + cos_v));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void GameMessages::SendSetPlayerControlScheme(Entity* entity, eControlScheme controlScheme) {
|
void GameMessages::SendSetPlayerControlScheme(Entity* entity, eControlScheme controlScheme) {
|
||||||
CBITSTREAM;
|
CBITSTREAM;
|
||||||
CMSGHEADER;
|
CMSGHEADER;
|
||||||
|
|||||||
@@ -153,7 +153,6 @@ namespace GameMessages {
|
|||||||
void SendStop2DAmbientSound(Entity* entity, bool force, std::string audioGUID, bool result = false);
|
void SendStop2DAmbientSound(Entity* entity, bool force, std::string audioGUID, bool result = false);
|
||||||
void SendPlay2DAmbientSound(Entity* entity, std::string audioGUID, bool result = false);
|
void SendPlay2DAmbientSound(Entity* entity, std::string audioGUID, bool result = false);
|
||||||
void SendSetNetworkScriptVar(Entity* entity, const SystemAddress& sysAddr, std::string data);
|
void SendSetNetworkScriptVar(Entity* entity, const SystemAddress& sysAddr, std::string data);
|
||||||
void SendDropClientLoot(Entity* entity, const LWOOBJID& sourceID, LOT item, int currency, NiPoint3 spawnPos = NiPoint3Constant::ZERO, int count = 1);
|
|
||||||
|
|
||||||
void SendSetPlayerControlScheme(Entity* entity, eControlScheme controlScheme);
|
void SendSetPlayerControlScheme(Entity* entity, eControlScheme controlScheme);
|
||||||
void SendPlayerReachedRespawnCheckpoint(Entity* entity, const NiPoint3& position, const NiQuaternion& rotation);
|
void SendPlayerReachedRespawnCheckpoint(Entity* entity, const NiPoint3& position, const NiQuaternion& rotation);
|
||||||
@@ -794,6 +793,7 @@ namespace GameMessages {
|
|||||||
AMFArrayValue* info{};
|
AMFArrayValue* info{};
|
||||||
AMFArrayValue* subCategory{};
|
AMFArrayValue* subCategory{};
|
||||||
bool bVerbose{};
|
bool bVerbose{};
|
||||||
|
LWOOBJID clientID{};
|
||||||
|
|
||||||
GetObjectReportInfo() : GameMsg(MessageType::Game::GET_OBJECT_REPORT_INFO, eGameMasterLevel::DEVELOPER) {}
|
GetObjectReportInfo() : GameMsg(MessageType::Game::GET_OBJECT_REPORT_INFO, eGameMasterLevel::DEVELOPER) {}
|
||||||
};
|
};
|
||||||
@@ -873,6 +873,22 @@ namespace GameMessages {
|
|||||||
bool bIgnoreChecks{ false };
|
bool bIgnoreChecks{ false };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct GetAngularVelocity : public GameMsg {
|
||||||
|
GetAngularVelocity() : GameMsg(MessageType::Game::GET_ANGULAR_VELOCITY) {}
|
||||||
|
|
||||||
|
NiPoint3 angVelocity{};
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SetAngularVelocity : public GameMsg {
|
||||||
|
SetAngularVelocity() : GameMsg(MessageType::Game::SET_ANGULAR_VELOCITY) {}
|
||||||
|
|
||||||
|
NiPoint3 angVelocity{};
|
||||||
|
|
||||||
|
bool bIgnoreDirtyFlags{};
|
||||||
|
|
||||||
|
bool bForceFlagDirty{};
|
||||||
|
};
|
||||||
|
|
||||||
struct DropClientLoot : public GameMsg {
|
struct DropClientLoot : public GameMsg {
|
||||||
DropClientLoot() : GameMsg(MessageType::Game::DROP_CLIENT_LOOT) {}
|
DropClientLoot() : GameMsg(MessageType::Game::DROP_CLIENT_LOOT) {}
|
||||||
|
|
||||||
|
|||||||
@@ -9,10 +9,12 @@
|
|||||||
#include "PropertyManagementComponent.h"
|
#include "PropertyManagementComponent.h"
|
||||||
#include "PlayerManager.h"
|
#include "PlayerManager.h"
|
||||||
#include "SimplePhysicsComponent.h"
|
#include "SimplePhysicsComponent.h"
|
||||||
|
#include "dMath.h"
|
||||||
|
|
||||||
#include "dChatFilter.h"
|
#include "dChatFilter.h"
|
||||||
|
|
||||||
#include "DluAssert.h"
|
#include "DluAssert.h"
|
||||||
|
#include "Loot.h"
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
void Strip::HandleMsg(AddStripMessage& msg) {
|
void Strip::HandleMsg(AddStripMessage& msg) {
|
||||||
@@ -104,7 +106,7 @@ void Strip::HandleMsg(GameMessages::ResetModelToDefaults& msg) {
|
|||||||
m_WaitingForAction = false;
|
m_WaitingForAction = false;
|
||||||
m_PausedTime = 0.0f;
|
m_PausedTime = 0.0f;
|
||||||
m_NextActionIndex = 0;
|
m_NextActionIndex = 0;
|
||||||
m_InActionMove = NiPoint3Constant::ZERO;
|
m_InActionTranslation = NiPoint3Constant::ZERO;
|
||||||
m_PreviousFramePosition = NiPoint3Constant::ZERO;
|
m_PreviousFramePosition = NiPoint3Constant::ZERO;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -148,7 +150,14 @@ void Strip::Spawn(LOT lot, Entity& entity) {
|
|||||||
// Spawns a specific drop for all
|
// Spawns a specific drop for all
|
||||||
void Strip::SpawnDrop(LOT dropLOT, Entity& entity) {
|
void Strip::SpawnDrop(LOT dropLOT, Entity& entity) {
|
||||||
for (auto* const player : PlayerManager::GetAllPlayers()) {
|
for (auto* const player : PlayerManager::GetAllPlayers()) {
|
||||||
GameMessages::SendDropClientLoot(player, entity.GetObjectID(), dropLOT, 0, entity.GetPosition());
|
GameMessages::DropClientLoot lootMsg{};
|
||||||
|
lootMsg.target = player->GetObjectID();
|
||||||
|
lootMsg.ownerID = player->GetObjectID();
|
||||||
|
lootMsg.sourceID = entity.GetObjectID();
|
||||||
|
lootMsg.item = dropLOT;
|
||||||
|
lootMsg.count = 1;
|
||||||
|
lootMsg.spawnPos = entity.GetPosition();
|
||||||
|
Loot::DropItem(*player, lootMsg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -159,40 +168,84 @@ void Strip::ProcNormalAction(float deltaTime, ModelComponent& modelComponent) {
|
|||||||
auto valueStr = nextAction.GetValueParameterString();
|
auto valueStr = nextAction.GetValueParameterString();
|
||||||
auto numberAsInt = static_cast<int32_t>(number);
|
auto numberAsInt = static_cast<int32_t>(number);
|
||||||
auto nextActionType = GetNextAction().GetType();
|
auto nextActionType = GetNextAction().GetType();
|
||||||
|
LOG("~number: %f, nextActionType: %s", static_cast<float>(number), nextActionType.data());
|
||||||
|
|
||||||
// TODO replace with switch case and nextActionType with enum
|
// TODO replace with switch case and nextActionType with enum
|
||||||
/* BEGIN Move */
|
/* BEGIN Move */
|
||||||
if (nextActionType == "MoveRight" || nextActionType == "MoveLeft") {
|
if (nextActionType == "MoveRight" || nextActionType == "MoveLeft") {
|
||||||
|
m_IsRotating = false;
|
||||||
// X axis
|
// X axis
|
||||||
bool isMoveLeft = nextActionType == "MoveLeft";
|
bool isMoveLeft = nextActionType == "MoveLeft";
|
||||||
int negative = isMoveLeft ? -1 : 1;
|
int negative = isMoveLeft ? -1 : 1;
|
||||||
// Default velocity is 3 units per second.
|
// Default velocity is 3 units per second.
|
||||||
if (modelComponent.TrySetVelocity(NiPoint3Constant::UNIT_X * negative)) {
|
if (modelComponent.TrySetVelocity(NiPoint3Constant::UNIT_X * negative)) {
|
||||||
m_PreviousFramePosition = entity.GetPosition();
|
m_PreviousFramePosition = entity.GetPosition();
|
||||||
m_InActionMove.x = isMoveLeft ? -number : number;
|
m_InActionTranslation.x = isMoveLeft ? -number : number;
|
||||||
}
|
}
|
||||||
} else if (nextActionType == "FlyUp" || nextActionType == "FlyDown") {
|
} else if (nextActionType == "FlyUp" || nextActionType == "FlyDown") {
|
||||||
|
m_IsRotating = false;
|
||||||
// Y axis
|
// Y axis
|
||||||
bool isFlyDown = nextActionType == "FlyDown";
|
bool isFlyDown = nextActionType == "FlyDown";
|
||||||
int negative = isFlyDown ? -1 : 1;
|
int negative = isFlyDown ? -1 : 1;
|
||||||
// Default velocity is 3 units per second.
|
// Default velocity is 3 units per second.
|
||||||
if (modelComponent.TrySetVelocity(NiPoint3Constant::UNIT_Y * negative)) {
|
if (modelComponent.TrySetVelocity(NiPoint3Constant::UNIT_Y * negative)) {
|
||||||
m_PreviousFramePosition = entity.GetPosition();
|
m_PreviousFramePosition = entity.GetPosition();
|
||||||
m_InActionMove.y = isFlyDown ? -number : number;
|
m_InActionTranslation.y = isFlyDown ? -number : number;
|
||||||
}
|
}
|
||||||
|
|
||||||
} else if (nextActionType == "MoveForward" || nextActionType == "MoveBackward") {
|
} else if (nextActionType == "MoveForward" || nextActionType == "MoveBackward") {
|
||||||
|
m_IsRotating = false;
|
||||||
// Z axis
|
// Z axis
|
||||||
bool isMoveBackward = nextActionType == "MoveBackward";
|
bool isMoveBackward = nextActionType == "MoveBackward";
|
||||||
int negative = isMoveBackward ? -1 : 1;
|
int negative = isMoveBackward ? -1 : 1;
|
||||||
// Default velocity is 3 units per second.
|
// Default velocity is 3 units per second.
|
||||||
if (modelComponent.TrySetVelocity(NiPoint3Constant::UNIT_Z * negative)) {
|
if (modelComponent.TrySetVelocity(NiPoint3Constant::UNIT_Z * negative)) {
|
||||||
m_PreviousFramePosition = entity.GetPosition();
|
m_PreviousFramePosition = entity.GetPosition();
|
||||||
m_InActionMove.z = isMoveBackward ? -number : number;
|
m_InActionTranslation.z = isMoveBackward ? -number : number;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/* END Move */
|
/* END Move */
|
||||||
|
|
||||||
|
/* BEGIN Rotate */
|
||||||
|
else if (nextActionType == "Spin" || nextActionType == "SpinNegative") {
|
||||||
|
const float radians = Math::DegToRad(number);
|
||||||
|
bool isSpinNegative = nextActionType == "SpinNegative";
|
||||||
|
float negative = isSpinNegative ? -0.261799f : 0.261799f;
|
||||||
|
|
||||||
|
// Default angular velocity is 3 units per second.
|
||||||
|
if (modelComponent.TrySetAngularVelocity(NiPoint3Constant::UNIT_Y * negative)) {
|
||||||
|
m_IsRotating = true;
|
||||||
|
m_InActionTranslation.y = isSpinNegative ? -number : number;
|
||||||
|
m_PreviousFrameRotation = entity.GetRotation();
|
||||||
|
// d/vi = t
|
||||||
|
// radians/velocity = time
|
||||||
|
// only care about the time, direction is irrelevant here
|
||||||
|
}
|
||||||
|
} else if (nextActionType == "Tilt" || nextActionType == "TiltNegative") {
|
||||||
|
const float radians = Math::DegToRad(number);
|
||||||
|
bool isRotateLeft = nextActionType == "TiltNegative";
|
||||||
|
float negative = isRotateLeft ? -0.261799f : 0.261799f;
|
||||||
|
|
||||||
|
// Default angular velocity is 3 units per second.
|
||||||
|
if (modelComponent.TrySetAngularVelocity(NiPoint3Constant::UNIT_X * negative)) {
|
||||||
|
m_IsRotating = true;
|
||||||
|
m_InActionTranslation.x = isRotateLeft ? -number : number;
|
||||||
|
m_PreviousFrameRotation = entity.GetRotation();
|
||||||
|
}
|
||||||
|
} else if (nextActionType == "Roll" || nextActionType == "RollNegative") {
|
||||||
|
const float radians = Math::DegToRad(number);
|
||||||
|
bool isRotateDown = nextActionType == "RollNegative";
|
||||||
|
float negative = isRotateDown ? -0.261799f : 0.261799f;
|
||||||
|
|
||||||
|
// Default angular velocity is 3 units per second.
|
||||||
|
if (modelComponent.TrySetAngularVelocity(NiPoint3Constant::UNIT_Z * negative)) {
|
||||||
|
m_IsRotating = true;
|
||||||
|
m_InActionTranslation.z = isRotateDown ? -number : number;
|
||||||
|
m_PreviousFrameRotation = entity.GetRotation();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* END Rotate */
|
||||||
|
|
||||||
/* BEGIN Navigation */
|
/* BEGIN Navigation */
|
||||||
else if (nextActionType == "SetSpeed") {
|
else if (nextActionType == "SetSpeed") {
|
||||||
modelComponent.SetSpeed(number);
|
modelComponent.SetSpeed(number);
|
||||||
@@ -277,36 +330,37 @@ void Strip::RemoveStates(ModelComponent& modelComponent) const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool Strip::CheckMovement(float deltaTime, ModelComponent& modelComponent) {
|
bool Strip::CheckMovement(float deltaTime, ModelComponent& modelComponent) {
|
||||||
|
if (m_IsRotating) return true;
|
||||||
|
|
||||||
auto& entity = *modelComponent.GetParent();
|
auto& entity = *modelComponent.GetParent();
|
||||||
const auto& currentPos = entity.GetPosition();
|
const auto& currentPos = entity.GetPosition();
|
||||||
const auto diff = currentPos - m_PreviousFramePosition;
|
const auto diff = currentPos - m_PreviousFramePosition;
|
||||||
const auto [moveX, moveY, moveZ] = m_InActionMove;
|
const auto [moveX, moveY, moveZ] = m_InActionTranslation;
|
||||||
m_PreviousFramePosition = currentPos;
|
m_PreviousFramePosition = currentPos;
|
||||||
|
|
||||||
// Only want to subtract from the move if one is being performed.
|
// Only want to subtract from the move if one is being performed.
|
||||||
// Starts at true because we may not be doing a move at all.
|
// Starts at true because we may not be doing a move at all.
|
||||||
// If one is being done, then one of the move_ variables will be non-zero
|
// If one is being done, then one of the move_ variables will be non-zero
|
||||||
bool moveFinished = true;
|
bool moveFinished = true;
|
||||||
NiPoint3 finalPositionAdjustment = NiPoint3Constant::ZERO;
|
NiPoint3 finalPositionAdjustment = NiPoint3Constant::ZERO;
|
||||||
if (moveX != 0.0f) {
|
if (moveX != 0.0f) {
|
||||||
m_InActionMove.x -= diff.x;
|
m_InActionTranslation.x -= diff.x;
|
||||||
// If the sign bit is different between the two numbers, then we have finished our move.
|
// If the sign bit is different between the two numbers, then we have finished our move.
|
||||||
moveFinished = std::signbit(m_InActionMove.x) != std::signbit(moveX);
|
moveFinished = std::signbit(m_InActionTranslation.x) != std::signbit(moveX);
|
||||||
finalPositionAdjustment.x = m_InActionMove.x;
|
finalPositionAdjustment.x = m_InActionTranslation.x;
|
||||||
} else if (moveY != 0.0f) {
|
} else if (moveY != 0.0f) {
|
||||||
m_InActionMove.y -= diff.y;
|
m_InActionTranslation.y -= diff.y;
|
||||||
// If the sign bit is different between the two numbers, then we have finished our move.
|
// If the sign bit is different between the two numbers, then we have finished our move.
|
||||||
moveFinished = std::signbit(m_InActionMove.y) != std::signbit(moveY);
|
moveFinished = std::signbit(m_InActionTranslation.y) != std::signbit(moveY);
|
||||||
finalPositionAdjustment.y = m_InActionMove.y;
|
finalPositionAdjustment.y = m_InActionTranslation.y;
|
||||||
} else if (moveZ != 0.0f) {
|
} else if (moveZ != 0.0f) {
|
||||||
m_InActionMove.z -= diff.z;
|
m_InActionTranslation.z -= diff.z;
|
||||||
// If the sign bit is different between the two numbers, then we have finished our move.
|
// If the sign bit is different between the two numbers, then we have finished our move.
|
||||||
moveFinished = std::signbit(m_InActionMove.z) != std::signbit(moveZ);
|
moveFinished = std::signbit(m_InActionTranslation.z) != std::signbit(moveZ);
|
||||||
finalPositionAdjustment.z = m_InActionMove.z;
|
finalPositionAdjustment.z = m_InActionTranslation.z;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Once done, set the in action move & velocity to zero
|
// Once done, set the in action move & velocity to zero
|
||||||
if (moveFinished && m_InActionMove != NiPoint3Constant::ZERO) {
|
if (moveFinished && m_InActionTranslation != NiPoint3Constant::ZERO) {
|
||||||
auto entityVelocity = entity.GetVelocity();
|
auto entityVelocity = entity.GetVelocity();
|
||||||
// Zero out only the velocity that was acted on
|
// Zero out only the velocity that was acted on
|
||||||
if (moveX != 0.0f) entityVelocity.x = 0.0f;
|
if (moveX != 0.0f) entityVelocity.x = 0.0f;
|
||||||
@@ -316,19 +370,82 @@ bool Strip::CheckMovement(float deltaTime, ModelComponent& modelComponent) {
|
|||||||
|
|
||||||
// Do the final adjustment so we will have moved exactly the requested units
|
// Do the final adjustment so we will have moved exactly the requested units
|
||||||
entity.SetPosition(entity.GetPosition() + finalPositionAdjustment);
|
entity.SetPosition(entity.GetPosition() + finalPositionAdjustment);
|
||||||
m_InActionMove = NiPoint3Constant::ZERO;
|
m_InActionTranslation = NiPoint3Constant::ZERO;
|
||||||
}
|
}
|
||||||
|
|
||||||
return moveFinished;
|
return moveFinished;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Strip::CheckRotation(float deltaTime, ModelComponent& modelComponent) {
|
||||||
|
if (!m_IsRotating) return true;
|
||||||
|
GameMessages::GetAngularVelocity getAngVel{};
|
||||||
|
getAngVel.target = modelComponent.GetParent()->GetObjectID();
|
||||||
|
getAngVel.Send();
|
||||||
|
const auto curRotation = modelComponent.GetParent()->GetRotation();
|
||||||
|
const auto diff = m_PreviousFrameRotation.Diff(curRotation).GetEulerAngles();
|
||||||
|
LOG("Diff: x=%f, y=%f, z=%f", std::abs(Math::RadToDeg(diff.x)), std::abs(Math::RadToDeg(diff.y)), std::abs(Math::RadToDeg(diff.z)));
|
||||||
|
LOG("Velocity: x=%f, y=%f, z=%f", Math::RadToDeg(getAngVel.angVelocity.x) * deltaTime, Math::RadToDeg(getAngVel.angVelocity.y) * deltaTime, Math::RadToDeg(getAngVel.angVelocity.z) * deltaTime);
|
||||||
|
m_PreviousFrameRotation = curRotation;
|
||||||
|
auto angVel = diff;
|
||||||
|
angVel.x = std::abs(Math::RadToDeg(angVel.x));
|
||||||
|
angVel.y = std::abs(Math::RadToDeg(angVel.y));
|
||||||
|
angVel.z = std::abs(Math::RadToDeg(angVel.z));
|
||||||
|
const auto [rotateX, rotateY, rotateZ] = m_InActionTranslation;
|
||||||
|
bool rotateFinished = true;
|
||||||
|
NiPoint3 finalRotationAdjustment = NiPoint3Constant::ZERO;
|
||||||
|
if (rotateX != 0.0f) {
|
||||||
|
m_InActionTranslation.x -= angVel.x;
|
||||||
|
rotateFinished = std::signbit(m_InActionTranslation.x) != std::signbit(rotateX);
|
||||||
|
finalRotationAdjustment.x = Math::DegToRad(m_InActionTranslation.x);
|
||||||
|
} else if (rotateY != 0.0f) {
|
||||||
|
m_InActionTranslation.y -= angVel.y;
|
||||||
|
rotateFinished = std::signbit(m_InActionTranslation.y) != std::signbit(rotateY);
|
||||||
|
finalRotationAdjustment.y = Math::DegToRad(m_InActionTranslation.y);
|
||||||
|
} else if (rotateZ != 0.0f) {
|
||||||
|
m_InActionTranslation.z -= angVel.z;
|
||||||
|
rotateFinished = std::signbit(m_InActionTranslation.z) != std::signbit(rotateZ);
|
||||||
|
finalRotationAdjustment.z = Math::DegToRad(m_InActionTranslation.z);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rotateFinished && m_InActionTranslation != NiPoint3Constant::ZERO) {
|
||||||
|
LOG("Rotation finished, zeroing angVel");
|
||||||
|
|
||||||
|
angVel.x = Math::DegToRad(angVel.x);
|
||||||
|
angVel.y = Math::DegToRad(angVel.y);
|
||||||
|
angVel.z = Math::DegToRad(angVel.z);
|
||||||
|
|
||||||
|
if (rotateX != 0.0f) getAngVel.angVelocity.x = 0.0f;
|
||||||
|
else if (rotateY != 0.0f) getAngVel.angVelocity.y = 0.0f;
|
||||||
|
else if (rotateZ != 0.0f) getAngVel.angVelocity.z = 0.0f;
|
||||||
|
|
||||||
|
GameMessages::SetAngularVelocity setAngVel{};
|
||||||
|
setAngVel.target = modelComponent.GetParent()->GetObjectID();
|
||||||
|
setAngVel.angVelocity = getAngVel.angVelocity;
|
||||||
|
setAngVel.Send();
|
||||||
|
|
||||||
|
// Do the final adjustment so we will have rotated exactly the requested units
|
||||||
|
auto currentRot = modelComponent.GetParent()->GetRotation();
|
||||||
|
NiQuaternion finalAdjustment = NiQuaternion::FromEulerAngles(finalRotationAdjustment);
|
||||||
|
currentRot *= finalAdjustment;
|
||||||
|
currentRot.Normalize();
|
||||||
|
modelComponent.GetParent()->SetRotation(currentRot);
|
||||||
|
|
||||||
|
m_InActionTranslation = NiPoint3Constant::ZERO;
|
||||||
|
m_IsRotating = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG("angVel: x=%f, y=%f, z=%f", m_InActionTranslation.x, m_InActionTranslation.y, m_InActionTranslation.z);
|
||||||
|
return rotateFinished;
|
||||||
|
}
|
||||||
|
|
||||||
void Strip::Update(float deltaTime, ModelComponent& modelComponent) {
|
void Strip::Update(float deltaTime, ModelComponent& modelComponent) {
|
||||||
// No point in running a strip with only one action.
|
// No point in running a strip with only one action.
|
||||||
// Strips are also designed to have 2 actions or more to run.
|
// Strips are also designed to have 2 actions or more to run.
|
||||||
if (!HasMinimumActions()) return;
|
if (!HasMinimumActions()) return;
|
||||||
|
|
||||||
// Return if this strip has an active movement action
|
// Return if this strip has an active movement or rotation action
|
||||||
if (!CheckMovement(deltaTime, modelComponent)) return;
|
if (!CheckMovement(deltaTime, modelComponent)) return;
|
||||||
|
if (!CheckRotation(deltaTime, modelComponent)) return;
|
||||||
|
|
||||||
// Don't run this strip if we're paused.
|
// Don't run this strip if we're paused.
|
||||||
m_PausedTime -= deltaTime;
|
m_PausedTime -= deltaTime;
|
||||||
@@ -348,7 +465,6 @@ void Strip::Update(float deltaTime, ModelComponent& modelComponent) {
|
|||||||
if (m_NextActionIndex == 0) {
|
if (m_NextActionIndex == 0) {
|
||||||
if (nextAction.GetType() == "OnInteract") {
|
if (nextAction.GetType() == "OnInteract") {
|
||||||
modelComponent.AddInteract();
|
modelComponent.AddInteract();
|
||||||
|
|
||||||
} else if (nextAction.GetType() == "OnChat") {
|
} else if (nextAction.GetType() == "OnChat") {
|
||||||
// logic here if needed
|
// logic here if needed
|
||||||
} else if (nextAction.GetType() == "OnAttack") {
|
} else if (nextAction.GetType() == "OnAttack") {
|
||||||
|
|||||||
@@ -33,6 +33,10 @@ public:
|
|||||||
// Checks the movement logic for whether or not to proceed
|
// Checks the movement logic for whether or not to proceed
|
||||||
// Returns true if the movement can continue, false if it needs to wait more.
|
// Returns true if the movement can continue, false if it needs to wait more.
|
||||||
bool CheckMovement(float deltaTime, ModelComponent& modelComponent);
|
bool CheckMovement(float deltaTime, ModelComponent& modelComponent);
|
||||||
|
|
||||||
|
// Checks the rotation logic for whether or not to proceed
|
||||||
|
// Returns true if the rotation can continue, false if it needs to wait more.
|
||||||
|
bool CheckRotation(float deltaTime, ModelComponent& modelComponent);
|
||||||
void Update(float deltaTime, ModelComponent& modelComponent);
|
void Update(float deltaTime, ModelComponent& modelComponent);
|
||||||
void SpawnDrop(LOT dropLOT, Entity& entity);
|
void SpawnDrop(LOT dropLOT, Entity& entity);
|
||||||
void ProcNormalAction(float deltaTime, ModelComponent& modelComponent);
|
void ProcNormalAction(float deltaTime, ModelComponent& modelComponent);
|
||||||
@@ -47,6 +51,9 @@ private:
|
|||||||
// Indicates this Strip is waiting for an action to be taken upon it to progress to its actions
|
// Indicates this Strip is waiting for an action to be taken upon it to progress to its actions
|
||||||
bool m_WaitingForAction{ false };
|
bool m_WaitingForAction{ false };
|
||||||
|
|
||||||
|
// True if this strip is currently rotating
|
||||||
|
bool m_IsRotating{ false };
|
||||||
|
|
||||||
// The amount of time this strip is paused for. Any interactions with this strip should be bounced if this is greater than 0.
|
// The amount of time this strip is paused for. Any interactions with this strip should be bounced if this is greater than 0.
|
||||||
// Actions that do not use time do not use this (ex. positions).
|
// Actions that do not use time do not use this (ex. positions).
|
||||||
float m_PausedTime{ 0.0f };
|
float m_PausedTime{ 0.0f };
|
||||||
@@ -60,13 +67,17 @@ private:
|
|||||||
// The location of this strip on the UGBehaviorEditor UI
|
// The location of this strip on the UGBehaviorEditor UI
|
||||||
StripUiPosition m_Position;
|
StripUiPosition m_Position;
|
||||||
|
|
||||||
// The current actions remaining distance to the target
|
// The current actions remaining translation to the target
|
||||||
// Only 1 of these vertexs' will be active at once for any given strip.
|
// Only 1 of these vertexs' will be active at once for any given strip.
|
||||||
NiPoint3 m_InActionMove{};
|
NiPoint3 m_InActionTranslation{};
|
||||||
|
|
||||||
// The position of the parent model on the previous frame
|
// The position of the parent model on the previous frame
|
||||||
NiPoint3 m_PreviousFramePosition{};
|
NiPoint3 m_PreviousFramePosition{};
|
||||||
|
|
||||||
|
NiPoint3 m_RotationRemaining{};
|
||||||
|
|
||||||
|
NiQuaternion m_PreviousFrameRotation{};
|
||||||
|
|
||||||
NiPoint3 m_SavedVelocity{};
|
NiPoint3 m_SavedVelocity{};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -93,17 +93,19 @@ std::map<LOT, LootDropInfo> RollLootMatrix(uint32_t matrixIndex) {
|
|||||||
|
|
||||||
// Generates a 'random' final position for the loot drop based on its input spawn position.
|
// Generates a 'random' final position for the loot drop based on its input spawn position.
|
||||||
void CalcFinalDropPos(GameMessages::DropClientLoot& lootMsg) {
|
void CalcFinalDropPos(GameMessages::DropClientLoot& lootMsg) {
|
||||||
lootMsg.bUsePosition = true;
|
if (lootMsg.spawnPos != NiPoint3Constant::ZERO) {
|
||||||
|
lootMsg.bUsePosition = true;
|
||||||
|
|
||||||
//Calculate where the loot will go:
|
//Calculate where the loot will go:
|
||||||
uint16_t degree = GeneralUtils::GenerateRandomNumber<uint16_t>(0, 360);
|
uint16_t degree = GeneralUtils::GenerateRandomNumber<uint16_t>(0, 360);
|
||||||
|
|
||||||
double rad = degree * 3.14 / 180;
|
double rad = degree * 3.14 / 180;
|
||||||
double sin_v = sin(rad) * 4.2;
|
double sin_v = sin(rad) * 4.2;
|
||||||
double cos_v = cos(rad) * 4.2;
|
double cos_v = cos(rad) * 4.2;
|
||||||
|
|
||||||
const auto [x, y, z] = lootMsg.spawnPos;
|
const auto [x, y, z] = lootMsg.spawnPos;
|
||||||
lootMsg.finalPosition = NiPoint3(static_cast<float>(x + sin_v), y, static_cast<float>(z + cos_v));
|
lootMsg.finalPosition = NiPoint3(static_cast<float>(x + sin_v), y, static_cast<float>(z + cos_v));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Visually drop the loot to all team members, though only the lootMsg.ownerID can pick it up
|
// Visually drop the loot to all team members, though only the lootMsg.ownerID can pick it up
|
||||||
@@ -363,6 +365,7 @@ void DropLoot(Entity* player, const LWOOBJID source, const std::map<LOT, LootDro
|
|||||||
lootMsg.spawnPos = spawnPosition;
|
lootMsg.spawnPos = spawnPosition;
|
||||||
lootMsg.sourceID = source;
|
lootMsg.sourceID = source;
|
||||||
lootMsg.item = LOT_NULL;
|
lootMsg.item = LOT_NULL;
|
||||||
|
CalcFinalDropPos(lootMsg);
|
||||||
lootMsg.Send();
|
lootMsg.Send();
|
||||||
const auto* const memberEntity = Game::entityManager->GetEntity(member);
|
const auto* const memberEntity = Game::entityManager->GetEntity(member);
|
||||||
if (memberEntity) lootMsg.Send(memberEntity->GetSystemAddress());
|
if (memberEntity) lootMsg.Send(memberEntity->GetSystemAddress());
|
||||||
@@ -375,6 +378,7 @@ void DropLoot(Entity* player, const LWOOBJID source, const std::map<LOT, LootDro
|
|||||||
lootMsg.spawnPos = spawnPosition;
|
lootMsg.spawnPos = spawnPosition;
|
||||||
lootMsg.sourceID = source;
|
lootMsg.sourceID = source;
|
||||||
lootMsg.item = LOT_NULL;
|
lootMsg.item = LOT_NULL;
|
||||||
|
CalcFinalDropPos(lootMsg);
|
||||||
lootMsg.Send();
|
lootMsg.Send();
|
||||||
lootMsg.Send(player->GetSystemAddress());
|
lootMsg.Send(player->GetSystemAddress());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
#include "eReplicaComponentType.h"
|
#include "eReplicaComponentType.h"
|
||||||
#include "RenderComponent.h"
|
#include "RenderComponent.h"
|
||||||
#include "eTerminateType.h"
|
#include "eTerminateType.h"
|
||||||
|
#include "Loot.h"
|
||||||
|
|
||||||
void GfTikiTorch::OnStartup(Entity* self) {
|
void GfTikiTorch::OnStartup(Entity* self) {
|
||||||
LightTorch(self);
|
LightTorch(self);
|
||||||
@@ -22,7 +23,14 @@ void GfTikiTorch::OnUse(Entity* self, Entity* killer) {
|
|||||||
self->SetI64(u"userID", killer->GetObjectID());
|
self->SetI64(u"userID", killer->GetObjectID());
|
||||||
|
|
||||||
for (int i = 0; i < m_numspawn; i++) {
|
for (int i = 0; i < m_numspawn; i++) {
|
||||||
GameMessages::SendDropClientLoot(killer, self->GetObjectID(), 935, 0, self->GetPosition());
|
GameMessages::DropClientLoot lootMsg{};
|
||||||
|
lootMsg.target = killer->GetObjectID();
|
||||||
|
lootMsg.ownerID = killer->GetObjectID();
|
||||||
|
lootMsg.sourceID = self->GetObjectID();
|
||||||
|
lootMsg.item = 935;
|
||||||
|
lootMsg.count = 1;
|
||||||
|
lootMsg.spawnPos = self->GetPosition();
|
||||||
|
Loot::DropItem(*killer, lootMsg);
|
||||||
}
|
}
|
||||||
|
|
||||||
self->AddTimer("InteractionCooldown", 4);
|
self->AddTimer("InteractionCooldown", 4);
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
#include "GameMessages.h"
|
#include "GameMessages.h"
|
||||||
#include "SkillComponent.h"
|
#include "SkillComponent.h"
|
||||||
#include "TeamManager.h"
|
#include "TeamManager.h"
|
||||||
|
#include "Loot.h"
|
||||||
|
|
||||||
void AgSurvivalBuffStation::OnQuickBuildComplete(Entity* self, Entity* target) {
|
void AgSurvivalBuffStation::OnQuickBuildComplete(Entity* self, Entity* target) {
|
||||||
auto destroyableComponent = self->GetComponent<DestroyableComponent>();
|
auto destroyableComponent = self->GetComponent<DestroyableComponent>();
|
||||||
@@ -55,7 +56,14 @@ void AgSurvivalBuffStation::OnTimerDone(Entity* self, std::string timerName) {
|
|||||||
for (auto memberID : team) {
|
for (auto memberID : team) {
|
||||||
auto member = Game::entityManager->GetEntity(memberID);
|
auto member = Game::entityManager->GetEntity(memberID);
|
||||||
if (member != nullptr && !member->GetIsDead()) {
|
if (member != nullptr && !member->GetIsDead()) {
|
||||||
GameMessages::SendDropClientLoot(member, self->GetObjectID(), powerupToDrop, 0, self->GetPosition());
|
GameMessages::DropClientLoot lootMsg{};
|
||||||
|
lootMsg.target = member->GetObjectID();
|
||||||
|
lootMsg.ownerID = member->GetObjectID();
|
||||||
|
lootMsg.sourceID = self->GetObjectID();
|
||||||
|
lootMsg.item = powerupToDrop;
|
||||||
|
lootMsg.count = 1;
|
||||||
|
lootMsg.spawnPos = self->GetPosition();
|
||||||
|
Loot::DropItem(*member, lootMsg, true, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
#include "EntityInfo.h"
|
#include "EntityInfo.h"
|
||||||
#include "DestroyableComponent.h"
|
#include "DestroyableComponent.h"
|
||||||
#include "eReplicaComponentType.h"
|
#include "eReplicaComponentType.h"
|
||||||
|
#include "Loot.h"
|
||||||
|
|
||||||
void AgImagSmashable::OnDie(Entity* self, Entity* killer) {
|
void AgImagSmashable::OnDie(Entity* self, Entity* killer) {
|
||||||
bool maxImagGreaterThanZero = false;
|
bool maxImagGreaterThanZero = false;
|
||||||
@@ -18,7 +19,14 @@ void AgImagSmashable::OnDie(Entity* self, Entity* killer) {
|
|||||||
if (maxImagGreaterThanZero) {
|
if (maxImagGreaterThanZero) {
|
||||||
int amount = GeneralUtils::GenerateRandomNumber<int>(0, 3);
|
int amount = GeneralUtils::GenerateRandomNumber<int>(0, 3);
|
||||||
for (int i = 0; i < amount; ++i) {
|
for (int i = 0; i < amount; ++i) {
|
||||||
GameMessages::SendDropClientLoot(killer, self->GetObjectID(), 935, 0, self->GetPosition());
|
GameMessages::DropClientLoot lootMsg{};
|
||||||
|
lootMsg.target = killer->GetObjectID();
|
||||||
|
lootMsg.ownerID = killer->GetObjectID();
|
||||||
|
lootMsg.sourceID = self->GetObjectID();
|
||||||
|
lootMsg.item = 935;
|
||||||
|
lootMsg.count = 1;
|
||||||
|
lootMsg.spawnPos = self->GetPosition();
|
||||||
|
Loot::DropItem(*killer, lootMsg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -55,36 +55,20 @@ void GfBanana::OnHit(Entity* self, Entity* attacker) {
|
|||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
bananaEntity->Smash(LWOOBJID_EMPTY, eKillType::SILENT);
|
||||||
|
|
||||||
bananaEntity->SetPosition(bananaEntity->GetPosition() - NiPoint3Constant::UNIT_Y * 8);
|
|
||||||
|
|
||||||
auto* bananaDestroyable = bananaEntity->GetComponent<DestroyableComponent>();
|
|
||||||
|
|
||||||
bananaDestroyable->SetHealth(0);
|
|
||||||
|
|
||||||
bananaDestroyable->Smash(attacker->GetObjectID());
|
|
||||||
|
|
||||||
/*
|
|
||||||
auto position = self->GetPosition();
|
|
||||||
const auto rotation = self->GetRotation();
|
const auto rotation = self->GetRotation();
|
||||||
|
EntityInfo info{};
|
||||||
position.y += 12;
|
|
||||||
position.x -= rotation.GetRightVector().x * 5;
|
|
||||||
position.z -= rotation.GetRightVector().z * 5;
|
|
||||||
|
|
||||||
EntityInfo info {};
|
|
||||||
|
|
||||||
info.pos = position;
|
|
||||||
info.rot = rotation;
|
|
||||||
info.lot = 6718;
|
info.lot = 6718;
|
||||||
|
info.pos = self->GetPosition();
|
||||||
|
info.pos.y += 12;
|
||||||
|
info.pos.x -= QuatUtils::Right(rotation).x * 5;
|
||||||
|
info.pos.z -= QuatUtils::Right(rotation).z * 5;
|
||||||
|
info.rot = rotation;
|
||||||
info.spawnerID = self->GetObjectID();
|
info.spawnerID = self->GetObjectID();
|
||||||
|
info.settings = { new LDFData<uint32_t>(u"motionType", 5) };
|
||||||
auto* entity = Game::entityManager->CreateEntity(info);
|
auto* const newEn = Game::entityManager->CreateEntity(info, nullptr, self);
|
||||||
|
Game::entityManager->ConstructEntity(newEn);
|
||||||
Game::entityManager->ConstructEntity(entity, UNASSIGNED_SYSTEM_ADDRESS);
|
|
||||||
*/
|
|
||||||
|
|
||||||
Game::entityManager->SerializeEntity(self);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void GfBanana::OnTimerDone(Entity* self, std::string timerName) {
|
void GfBanana::OnTimerDone(Entity* self, std::string timerName) {
|
||||||
|
|||||||
@@ -1,5 +1,9 @@
|
|||||||
#include "GfBananaCluster.h"
|
#include "GfBananaCluster.h"
|
||||||
#include "Entity.h"
|
#include "Entity.h"
|
||||||
|
#include "dpWorld.h"
|
||||||
|
#include "dNavMesh.h"
|
||||||
|
#include "Loot.h"
|
||||||
|
#include "DestroyableComponent.h"
|
||||||
|
|
||||||
void GfBananaCluster::OnStartup(Entity* self) {
|
void GfBananaCluster::OnStartup(Entity* self) {
|
||||||
self->AddTimer("startup", 100);
|
self->AddTimer("startup", 100);
|
||||||
@@ -10,3 +14,21 @@ void GfBananaCluster::OnTimerDone(Entity* self, std::string timerName) {
|
|||||||
self->ScheduleKillAfterUpdate(nullptr);
|
self->ScheduleKillAfterUpdate(nullptr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Hack in banana loot dropping from tree area since it seemed to do that in live for some reason
|
||||||
|
void GfBananaCluster::OnHit(Entity* self, Entity* attacker) {
|
||||||
|
auto* parentEntity = self->GetParentEntity();
|
||||||
|
GameMessages::GetPosition posMsg{};
|
||||||
|
if (parentEntity) {
|
||||||
|
posMsg.target = parentEntity->GetObjectID();
|
||||||
|
}
|
||||||
|
posMsg.Send();
|
||||||
|
|
||||||
|
const auto rotation = parentEntity ? parentEntity->GetRotation() : self->GetRotation();
|
||||||
|
|
||||||
|
if (dpWorld::GetNavMesh()) posMsg.pos.y = dpWorld::GetNavMesh()->GetHeightAtPoint(posMsg.pos) + 3.0f;
|
||||||
|
else posMsg.pos = posMsg.pos - (NiPoint3Constant::UNIT_Y * 8);
|
||||||
|
posMsg.pos.x -= QuatUtils::Right(rotation).x * 5;
|
||||||
|
posMsg.pos.z -= QuatUtils::Right(rotation).z * 5;
|
||||||
|
self->SetPosition(posMsg.pos);
|
||||||
|
}
|
||||||
|
|||||||
@@ -7,4 +7,5 @@ public:
|
|||||||
void OnStartup(Entity* self) override;
|
void OnStartup(Entity* self) override;
|
||||||
|
|
||||||
void OnTimerDone(Entity* self, std::string timerName) override;
|
void OnTimerDone(Entity* self, std::string timerName) override;
|
||||||
|
void OnHit(Entity* self, Entity* attacker) override;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
#include "NsQbImaginationStatue.h"
|
#include "NsQbImaginationStatue.h"
|
||||||
#include "EntityManager.h"
|
#include "EntityManager.h"
|
||||||
#include "GameMessages.h"
|
#include "GameMessages.h"
|
||||||
|
#include "Loot.h"
|
||||||
|
|
||||||
void NsQbImaginationStatue::OnStartup(Entity* self) {
|
void NsQbImaginationStatue::OnStartup(Entity* self) {
|
||||||
|
|
||||||
@@ -35,6 +36,12 @@ void NsQbImaginationStatue::SpawnLoot(Entity* self) {
|
|||||||
|
|
||||||
if (player == nullptr) return;
|
if (player == nullptr) return;
|
||||||
|
|
||||||
GameMessages::SendDropClientLoot(player, self->GetObjectID(), 935, 0);
|
GameMessages::DropClientLoot lootMsg{};
|
||||||
GameMessages::SendDropClientLoot(player, self->GetObjectID(), 935, 0);
|
lootMsg.target = player->GetObjectID();
|
||||||
|
lootMsg.ownerID = player->GetObjectID();
|
||||||
|
lootMsg.sourceID = self->GetObjectID();
|
||||||
|
lootMsg.item = 935;
|
||||||
|
lootMsg.count = 1;
|
||||||
|
Loot::DropItem(*player, lootMsg);
|
||||||
|
Loot::DropItem(*player, lootMsg);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user