mirror of
https://github.com/DarkflameUniverse/DarkflameServer.git
synced 2025-12-05 23:58:30 +00:00
Merge remote-tracking branch 'upstream/main' into PetFixes
This commit is contained in:
@@ -26,7 +26,6 @@ target_include_directories(dGameBase PUBLIC "." "dEntity"
|
||||
"${PROJECT_SOURCE_DIR}/dDatabase/CDClientDatabase/CDClientTables"
|
||||
"${PROJECT_SOURCE_DIR}/dDatabase/GameDatabase"
|
||||
"${PROJECT_SOURCE_DIR}/dDatabase/GameDatabase/ITables"
|
||||
"${PROJECT_SOURCE_DIR}/thirdparty/mariadb-connector-cpp/include"
|
||||
# dPhysics
|
||||
"${PROJECT_SOURCE_DIR}/thirdparty/recastnavigation/Recast/Include"
|
||||
"${PROJECT_SOURCE_DIR}/thirdparty/recastnavigation/Detour/Include"
|
||||
|
||||
@@ -97,6 +97,8 @@
|
||||
#include "CDSkillBehaviorTable.h"
|
||||
#include "CDZoneTableTable.h"
|
||||
|
||||
#include <ranges>
|
||||
|
||||
Observable<Entity*, const PositionUpdate&> Entity::OnPlayerPositionUpdate;
|
||||
|
||||
Entity::Entity(const LWOOBJID& objectID, EntityInfo info, User* parentUser, Entity* parentEntity) {
|
||||
@@ -286,8 +288,9 @@ void Entity::Initialize() {
|
||||
AddComponent<PropertyEntranceComponent>(propertyEntranceComponentID);
|
||||
}
|
||||
|
||||
if (compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::CONTROLLABLE_PHYSICS) > 0) {
|
||||
auto* controllablePhysics = AddComponent<ControllablePhysicsComponent>();
|
||||
const int32_t controllablePhysicsComponentID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::CONTROLLABLE_PHYSICS);
|
||||
if (controllablePhysicsComponentID > 0) {
|
||||
auto* controllablePhysics = AddComponent<ControllablePhysicsComponent>(controllablePhysicsComponentID);
|
||||
|
||||
if (m_Character) {
|
||||
controllablePhysics->LoadFromXml(m_Character->GetXMLDoc());
|
||||
@@ -330,16 +333,19 @@ void Entity::Initialize() {
|
||||
AddComponent<SimplePhysicsComponent>(simplePhysicsComponentID);
|
||||
}
|
||||
|
||||
if (compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::RIGID_BODY_PHANTOM_PHYSICS) > 0) {
|
||||
AddComponent<RigidbodyPhantomPhysicsComponent>();
|
||||
const int32_t rigidBodyPhantomPhysicsComponentID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::RIGID_BODY_PHANTOM_PHYSICS);
|
||||
if (rigidBodyPhantomPhysicsComponentID > 0) {
|
||||
AddComponent<RigidbodyPhantomPhysicsComponent>(rigidBodyPhantomPhysicsComponentID);
|
||||
}
|
||||
|
||||
if (markedAsPhantom || compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::PHANTOM_PHYSICS) > 0) {
|
||||
AddComponent<PhantomPhysicsComponent>()->SetPhysicsEffectActive(false);
|
||||
const int32_t phantomPhysicsComponentID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::PHANTOM_PHYSICS);
|
||||
if (markedAsPhantom || phantomPhysicsComponentID > 0) {
|
||||
AddComponent<PhantomPhysicsComponent>(phantomPhysicsComponentID)->SetPhysicsEffectActive(false);
|
||||
}
|
||||
|
||||
if (compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::HAVOK_VEHICLE_PHYSICS) > 0) {
|
||||
auto* havokVehiclePhysicsComponent = AddComponent<HavokVehiclePhysicsComponent>();
|
||||
const int32_t havokVehiclePhysicsComponentID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::HAVOK_VEHICLE_PHYSICS);
|
||||
if (havokVehiclePhysicsComponentID > 0) {
|
||||
auto* havokVehiclePhysicsComponent = AddComponent<HavokVehiclePhysicsComponent>(havokVehiclePhysicsComponentID);
|
||||
havokVehiclePhysicsComponent->SetPosition(m_DefaultPosition);
|
||||
havokVehiclePhysicsComponent->SetRotation(m_DefaultRotation);
|
||||
}
|
||||
@@ -2161,7 +2167,19 @@ void Entity::SetRespawnPos(const NiPoint3& position) {
|
||||
auto* characterComponent = GetComponent<CharacterComponent>();
|
||||
if (characterComponent) characterComponent->SetRespawnPos(position);
|
||||
}
|
||||
|
||||
void Entity::SetRespawnRot(const NiQuaternion& rotation) {
|
||||
auto* characterComponent = GetComponent<CharacterComponent>();
|
||||
if (characterComponent) characterComponent->SetRespawnRot(rotation);
|
||||
}
|
||||
|
||||
int32_t Entity::GetCollisionGroup() const {
|
||||
for (const auto* component : m_Components | std::views::values) {
|
||||
auto* compToCheck = dynamic_cast<const PhysicsComponent*>(component);
|
||||
if (compToCheck) {
|
||||
return compToCheck->GetCollisionGroup();
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -107,6 +107,11 @@ public:
|
||||
|
||||
const SystemAddress& GetSystemAddress() const;
|
||||
|
||||
// Returns the collision group for this entity.
|
||||
// Because the collision group is stored on a base component, this will look for a physics component
|
||||
// then return the collision group from that.
|
||||
int32_t GetCollisionGroup() const;
|
||||
|
||||
/**
|
||||
* Setters
|
||||
*/
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
#include "DestroyableComponent.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <ranges>
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
|
||||
@@ -27,7 +28,7 @@
|
||||
#include "CDPhysicsComponentTable.h"
|
||||
#include "dNavMesh.h"
|
||||
|
||||
BaseCombatAIComponent::BaseCombatAIComponent(Entity* parent, const uint32_t id): Component(parent) {
|
||||
BaseCombatAIComponent::BaseCombatAIComponent(Entity* parent, const uint32_t id) : Component(parent) {
|
||||
m_Target = LWOOBJID_EMPTY;
|
||||
m_DirtyStateOrTarget = true;
|
||||
m_State = AiState::spawn;
|
||||
@@ -37,6 +38,7 @@ BaseCombatAIComponent::BaseCombatAIComponent(Entity* parent, const uint32_t id):
|
||||
m_Disabled = false;
|
||||
m_SkillEntries = {};
|
||||
m_SoftTimer = 5.0f;
|
||||
m_ForcedTetherTime = 0.0f;
|
||||
|
||||
//Grab the aggro information from BaseCombatAI:
|
||||
auto componentQuery = CDClientDatabase::CreatePreppedStmt(
|
||||
@@ -170,6 +172,17 @@ void BaseCombatAIComponent::Update(const float deltaTime) {
|
||||
GameMessages::SendStopFXEffect(m_Parent, true, "tether");
|
||||
m_TetherEffectActive = false;
|
||||
}
|
||||
m_ForcedTetherTime -= deltaTime;
|
||||
if (m_ForcedTetherTime >= 0) return;
|
||||
}
|
||||
|
||||
for (auto entry = m_RemovedThreatList.begin(); entry != m_RemovedThreatList.end();) {
|
||||
entry->second -= deltaTime;
|
||||
if (entry->second <= 0.0f) {
|
||||
entry = m_RemovedThreatList.erase(entry);
|
||||
} else {
|
||||
++entry;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_SoftTimer <= 0.0f) {
|
||||
@@ -287,40 +300,7 @@ void BaseCombatAIComponent::CalculateCombat(const float deltaTime) {
|
||||
}
|
||||
|
||||
if (!m_TetherEffectActive && m_OutOfCombat && (m_OutOfCombatTime -= deltaTime) <= 0) {
|
||||
auto* destroyableComponent = m_Parent->GetComponent<DestroyableComponent>();
|
||||
|
||||
if (destroyableComponent != nullptr && destroyableComponent->HasFaction(4)) {
|
||||
auto serilizationRequired = false;
|
||||
|
||||
if (destroyableComponent->GetHealth() != destroyableComponent->GetMaxHealth()) {
|
||||
destroyableComponent->SetHealth(destroyableComponent->GetMaxHealth());
|
||||
|
||||
serilizationRequired = true;
|
||||
}
|
||||
|
||||
if (destroyableComponent->GetArmor() != destroyableComponent->GetMaxArmor()) {
|
||||
destroyableComponent->SetArmor(destroyableComponent->GetMaxArmor());
|
||||
|
||||
serilizationRequired = true;
|
||||
}
|
||||
|
||||
if (serilizationRequired) {
|
||||
Game::entityManager->SerializeEntity(m_Parent);
|
||||
}
|
||||
|
||||
GameMessages::SendPlayFXEffect(m_Parent->GetObjectID(), 6270, u"tether", "tether");
|
||||
|
||||
m_TetherEffectActive = true;
|
||||
|
||||
m_TetherTime = 3.0f;
|
||||
}
|
||||
|
||||
// Speed towards start position
|
||||
if (m_MovementAI != nullptr) {
|
||||
m_MovementAI->SetHaltDistance(0);
|
||||
m_MovementAI->SetMaxSpeed(m_PursuitSpeed);
|
||||
m_MovementAI->SetDestination(m_StartPosition);
|
||||
}
|
||||
TetherLogic();
|
||||
|
||||
m_OutOfCombat = false;
|
||||
m_OutOfCombatTime = 0.0f;
|
||||
@@ -499,7 +479,7 @@ std::vector<LWOOBJID> BaseCombatAIComponent::GetTargetWithinAggroRange() const {
|
||||
|
||||
const auto distance = Vector3::DistanceSquared(m_Parent->GetPosition(), other->GetPosition());
|
||||
|
||||
if (distance > m_AggroRadius * m_AggroRadius) continue;
|
||||
if (distance > m_AggroRadius * m_AggroRadius || m_RemovedThreatList.contains(id)) continue;
|
||||
|
||||
targets.push_back(id);
|
||||
}
|
||||
@@ -626,6 +606,7 @@ const NiPoint3& BaseCombatAIComponent::GetStartPosition() const {
|
||||
|
||||
void BaseCombatAIComponent::ClearThreat() {
|
||||
m_ThreatEntries.clear();
|
||||
m_Target = LWOOBJID_EMPTY;
|
||||
|
||||
m_DirtyThreat = true;
|
||||
}
|
||||
@@ -806,3 +787,55 @@ void BaseCombatAIComponent::Wake() {
|
||||
m_dpEntity->SetSleeping(false);
|
||||
m_dpEntityEnemy->SetSleeping(false);
|
||||
}
|
||||
|
||||
void BaseCombatAIComponent::TetherLogic() {
|
||||
auto* destroyableComponent = m_Parent->GetComponent<DestroyableComponent>();
|
||||
|
||||
if (destroyableComponent != nullptr && destroyableComponent->HasFaction(4)) {
|
||||
auto serilizationRequired = false;
|
||||
|
||||
if (destroyableComponent->GetHealth() != destroyableComponent->GetMaxHealth()) {
|
||||
destroyableComponent->SetHealth(destroyableComponent->GetMaxHealth());
|
||||
|
||||
serilizationRequired = true;
|
||||
}
|
||||
|
||||
if (destroyableComponent->GetArmor() != destroyableComponent->GetMaxArmor()) {
|
||||
destroyableComponent->SetArmor(destroyableComponent->GetMaxArmor());
|
||||
|
||||
serilizationRequired = true;
|
||||
}
|
||||
|
||||
if (serilizationRequired) {
|
||||
Game::entityManager->SerializeEntity(m_Parent);
|
||||
}
|
||||
|
||||
GameMessages::SendPlayFXEffect(m_Parent->GetObjectID(), 6270, u"tether", "tether");
|
||||
|
||||
m_TetherEffectActive = true;
|
||||
|
||||
m_TetherTime = 3.0f;
|
||||
}
|
||||
|
||||
// Speed towards start position
|
||||
if (m_MovementAI != nullptr) {
|
||||
m_MovementAI->SetHaltDistance(0);
|
||||
m_MovementAI->SetMaxSpeed(m_PursuitSpeed);
|
||||
m_MovementAI->SetDestination(m_StartPosition);
|
||||
}
|
||||
}
|
||||
|
||||
void BaseCombatAIComponent::ForceTether() {
|
||||
SetTarget(LWOOBJID_EMPTY);
|
||||
m_ThreatEntries.clear();
|
||||
TetherLogic();
|
||||
m_ForcedTetherTime = m_TetherTime;
|
||||
|
||||
SetAiState(AiState::aggro);
|
||||
}
|
||||
|
||||
void BaseCombatAIComponent::IgnoreThreat(const LWOOBJID threat, const float value) {
|
||||
m_RemovedThreatList[threat] = value;
|
||||
SetThreat(threat, 0.0f);
|
||||
m_Target = LWOOBJID_EMPTY;
|
||||
}
|
||||
|
||||
@@ -224,6 +224,16 @@ public:
|
||||
*/
|
||||
void Wake();
|
||||
|
||||
// Force this entity to tether and ignore all other actions
|
||||
void ForceTether();
|
||||
|
||||
// heals the entity to full health and armor
|
||||
// and tethers them to their spawn point
|
||||
void TetherLogic();
|
||||
|
||||
// Ignore a threat for a certain amount of time
|
||||
void IgnoreThreat(const LWOOBJID target, const float time);
|
||||
|
||||
private:
|
||||
/**
|
||||
* Returns the current target or the target that currently is the largest threat to this entity
|
||||
@@ -382,6 +392,12 @@ private:
|
||||
*/
|
||||
bool m_DirtyStateOrTarget = false;
|
||||
|
||||
// The amount of time the entity will be forced to tether for
|
||||
float m_ForcedTetherTime = 0.0f;
|
||||
|
||||
// The amount of time a removed threat will be ignored for.
|
||||
std::map<LWOOBJID, float> m_RemovedThreatList;
|
||||
|
||||
/**
|
||||
* Whether the current entity is a mech enemy, needed as mechs tether radius works differently
|
||||
* @return whether this entity is a mech
|
||||
|
||||
@@ -65,7 +65,6 @@ target_include_directories(dComponents PUBLIC "."
|
||||
"${PROJECT_SOURCE_DIR}/dDatabase/CDClientDatabase/CDClientTables"
|
||||
"${PROJECT_SOURCE_DIR}/dDatabase/GameDatabase"
|
||||
"${PROJECT_SOURCE_DIR}/dDatabase/GameDatabase/ITables"
|
||||
"${PROJECT_SOURCE_DIR}/thirdparty/mariadb-connector-cpp/include"
|
||||
# dPhysics (via dpWorld.h)
|
||||
"${PROJECT_SOURCE_DIR}/thirdparty/recastnavigation/Recast/Include"
|
||||
"${PROJECT_SOURCE_DIR}/thirdparty/recastnavigation/Detour/Include"
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
#include "LevelProgressionComponent.h"
|
||||
#include "eStateChangeType.h"
|
||||
|
||||
ControllablePhysicsComponent::ControllablePhysicsComponent(Entity* entity) : PhysicsComponent(entity) {
|
||||
ControllablePhysicsComponent::ControllablePhysicsComponent(Entity* entity, int32_t componentId) : PhysicsComponent(entity, componentId) {
|
||||
m_Velocity = {};
|
||||
m_AngularVelocity = {};
|
||||
m_InJetpackMode = false;
|
||||
|
||||
@@ -23,7 +23,7 @@ class ControllablePhysicsComponent : public PhysicsComponent {
|
||||
public:
|
||||
static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::CONTROLLABLE_PHYSICS;
|
||||
|
||||
ControllablePhysicsComponent(Entity* entity);
|
||||
ControllablePhysicsComponent(Entity* entity, int32_t componentId);
|
||||
~ControllablePhysicsComponent() override;
|
||||
|
||||
void Update(float deltaTime) override;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#include "HavokVehiclePhysicsComponent.h"
|
||||
#include "EntityManager.h"
|
||||
|
||||
HavokVehiclePhysicsComponent::HavokVehiclePhysicsComponent(Entity* parent) : PhysicsComponent(parent) {
|
||||
HavokVehiclePhysicsComponent::HavokVehiclePhysicsComponent(Entity* parent, int32_t componentId) : PhysicsComponent(parent, componentId) {
|
||||
m_Velocity = NiPoint3Constant::ZERO;
|
||||
m_AngularVelocity = NiPoint3Constant::ZERO;
|
||||
m_IsOnGround = true;
|
||||
|
||||
@@ -13,7 +13,7 @@ class HavokVehiclePhysicsComponent : public PhysicsComponent {
|
||||
public:
|
||||
static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::HAVOK_VEHICLE_PHYSICS;
|
||||
|
||||
HavokVehiclePhysicsComponent(Entity* parentEntity);
|
||||
HavokVehiclePhysicsComponent(Entity* parentEntity, int32_t componentId);
|
||||
|
||||
void Serialize(RakNet::BitStream& outBitStream, bool bIsInitialUpdate) override;
|
||||
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
#include "eStateChangeType.h"
|
||||
#include "eUseItemResponse.h"
|
||||
#include "Mail.h"
|
||||
#include "ProximityMonitorComponent.h"
|
||||
|
||||
#include "CDComponentsRegistryTable.h"
|
||||
#include "CDInventoryComponentTable.h"
|
||||
@@ -829,6 +830,30 @@ void InventoryComponent::EquipItem(Item* item, const bool skipChecks) {
|
||||
break;
|
||||
}
|
||||
|
||||
return;
|
||||
} else if (item->GetLot() == 8092) {
|
||||
// Trying to equip a car
|
||||
const auto proximityObjects = Game::entityManager->GetEntitiesByComponent(eReplicaComponentType::PROXIMITY_MONITOR);
|
||||
|
||||
// look for car instancers and check if we are in its setup range
|
||||
for (auto* const entity : proximityObjects) {
|
||||
if (!entity) continue;
|
||||
|
||||
auto* proximityMonitorComponent = entity->GetComponent<ProximityMonitorComponent>();
|
||||
if (!proximityMonitorComponent) continue;
|
||||
|
||||
if (proximityMonitorComponent->IsInProximity("Interaction_Distance", m_Parent->GetObjectID())) {
|
||||
// in the range of a car instancer
|
||||
entity->OnUse(m_Parent);
|
||||
GameMessages::UseItemOnClient itemMsg;
|
||||
itemMsg.target = entity->GetObjectID();
|
||||
itemMsg.itemLOT = item->GetLot();
|
||||
itemMsg.itemToUse = item->GetId();
|
||||
itemMsg.playerId = m_Parent->GetObjectID();
|
||||
itemMsg.Send(m_Parent->GetSystemAddress());
|
||||
break;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
#include "dpShapeBox.h"
|
||||
#include "dpShapeSphere.h"
|
||||
|
||||
PhantomPhysicsComponent::PhantomPhysicsComponent(Entity* parent) : PhysicsComponent(parent) {
|
||||
PhantomPhysicsComponent::PhantomPhysicsComponent(Entity* parent, int32_t componentId) : PhysicsComponent(parent, componentId) {
|
||||
m_Position = m_Parent->GetDefaultPosition();
|
||||
m_Rotation = m_Parent->GetDefaultRotation();
|
||||
m_Scale = m_Parent->GetDefaultScale();
|
||||
|
||||
@@ -30,7 +30,7 @@ class PhantomPhysicsComponent final : public PhysicsComponent {
|
||||
public:
|
||||
static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::PHANTOM_PHYSICS;
|
||||
|
||||
PhantomPhysicsComponent(Entity* parent);
|
||||
PhantomPhysicsComponent(Entity* parent, int32_t componentId);
|
||||
~PhantomPhysicsComponent() override;
|
||||
void Update(float deltaTime) override;
|
||||
void Serialize(RakNet::BitStream& outBitStream, bool bIsInitialUpdate) override;
|
||||
|
||||
@@ -14,10 +14,21 @@
|
||||
|
||||
#include "EntityInfo.h"
|
||||
|
||||
PhysicsComponent::PhysicsComponent(Entity* parent) : Component(parent) {
|
||||
PhysicsComponent::PhysicsComponent(Entity* parent, int32_t componentId) : Component(parent) {
|
||||
m_Position = NiPoint3Constant::ZERO;
|
||||
m_Rotation = NiQuaternionConstant::IDENTITY;
|
||||
m_DirtyPosition = false;
|
||||
|
||||
CDPhysicsComponentTable* physicsComponentTable = CDClientManager::GetTable<CDPhysicsComponentTable>();
|
||||
|
||||
if (physicsComponentTable) {
|
||||
auto* info = physicsComponentTable->GetByID(componentId);
|
||||
if (info) {
|
||||
m_CollisionGroup = info->collisionGroup;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_Parent->HasVar(u"CollisionGroupID")) m_CollisionGroup = m_Parent->GetVar<int32_t>(u"CollisionGroupID");
|
||||
}
|
||||
|
||||
void PhysicsComponent::Serialize(RakNet::BitStream& outBitStream, bool bIsInitialUpdate) {
|
||||
|
||||
@@ -15,7 +15,7 @@ class dpEntity;
|
||||
|
||||
class PhysicsComponent : public Component {
|
||||
public:
|
||||
PhysicsComponent(Entity* parent);
|
||||
PhysicsComponent(Entity* parent, int32_t componentId);
|
||||
virtual ~PhysicsComponent() = default;
|
||||
|
||||
void Serialize(RakNet::BitStream& outBitStream, bool bIsInitialUpdate) override;
|
||||
@@ -25,6 +25,9 @@ public:
|
||||
|
||||
const NiQuaternion& GetRotation() const { return m_Rotation; }
|
||||
virtual void SetRotation(const NiQuaternion& rot) { if (m_Rotation == rot) return; m_Rotation = rot; m_DirtyPosition = true; }
|
||||
|
||||
int32_t GetCollisionGroup() const noexcept { return m_CollisionGroup; }
|
||||
void SetCollisionGroup(int32_t group) noexcept { m_CollisionGroup = group; }
|
||||
protected:
|
||||
dpEntity* CreatePhysicsEntity(eReplicaComponentType type);
|
||||
|
||||
@@ -37,6 +40,8 @@ protected:
|
||||
NiQuaternion m_Rotation;
|
||||
|
||||
bool m_DirtyPosition;
|
||||
|
||||
int32_t m_CollisionGroup{};
|
||||
};
|
||||
|
||||
#endif //!__PHYSICSCOMPONENT__H__
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
#include "dpShapeSphere.h"
|
||||
#include"EntityInfo.h"
|
||||
|
||||
RigidbodyPhantomPhysicsComponent::RigidbodyPhantomPhysicsComponent(Entity* parent) : PhysicsComponent(parent) {
|
||||
RigidbodyPhantomPhysicsComponent::RigidbodyPhantomPhysicsComponent(Entity* parent, int32_t componentId) : PhysicsComponent(parent, componentId) {
|
||||
m_Position = m_Parent->GetDefaultPosition();
|
||||
m_Rotation = m_Parent->GetDefaultRotation();
|
||||
m_Scale = m_Parent->GetDefaultScale();
|
||||
|
||||
@@ -21,7 +21,7 @@ class RigidbodyPhantomPhysicsComponent : public PhysicsComponent {
|
||||
public:
|
||||
static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::RIGID_BODY_PHANTOM_PHYSICS;
|
||||
|
||||
RigidbodyPhantomPhysicsComponent(Entity* parent);
|
||||
RigidbodyPhantomPhysicsComponent(Entity* parent, int32_t componentId);
|
||||
|
||||
void Update(const float deltaTime) override;
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
|
||||
#include "Entity.h"
|
||||
|
||||
SimplePhysicsComponent::SimplePhysicsComponent(Entity* parent, uint32_t componentID) : PhysicsComponent(parent) {
|
||||
SimplePhysicsComponent::SimplePhysicsComponent(Entity* parent, int32_t componentID) : PhysicsComponent(parent, componentID) {
|
||||
m_Position = m_Parent->GetDefaultPosition();
|
||||
m_Rotation = m_Parent->GetDefaultRotation();
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@ class SimplePhysicsComponent : public PhysicsComponent {
|
||||
public:
|
||||
static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::SIMPLE_PHYSICS;
|
||||
|
||||
SimplePhysicsComponent(Entity* parent, uint32_t componentID);
|
||||
SimplePhysicsComponent(Entity* parent, int32_t componentID);
|
||||
~SimplePhysicsComponent() override;
|
||||
|
||||
void Serialize(RakNet::BitStream& outBitStream, bool bIsInitialUpdate) override;
|
||||
|
||||
@@ -6375,3 +6375,58 @@ void GameMessages::SendUpdateInventoryUi(LWOOBJID objectId, const SystemAddress&
|
||||
|
||||
SEND_PACKET;
|
||||
}
|
||||
|
||||
namespace GameMessages {
|
||||
void GameMsg::Send(const SystemAddress& sysAddr) const {
|
||||
CBITSTREAM;
|
||||
CMSGHEADER;
|
||||
|
||||
bitStream.Write(target); // Who this message will be sent to on the (a) client
|
||||
bitStream.Write(msgId); // the ID of this message
|
||||
|
||||
Serialize(bitStream); // write the message data
|
||||
|
||||
// Send to everyone if someone sent unassigned system address, or to one specific client.
|
||||
if (sysAddr == UNASSIGNED_SYSTEM_ADDRESS) {
|
||||
SEND_PACKET_BROADCAST;
|
||||
} else {
|
||||
SEND_PACKET;
|
||||
}
|
||||
}
|
||||
|
||||
void DisplayTooltip::Serialize(RakNet::BitStream& bitStream) const {
|
||||
bitStream.Write(doOrDie);
|
||||
bitStream.Write(noRepeat);
|
||||
bitStream.Write(noRevive);
|
||||
bitStream.Write(isPropertyTooltip);
|
||||
bitStream.Write(show);
|
||||
bitStream.Write(translate);
|
||||
bitStream.Write(time);
|
||||
bitStream.Write<int32_t>(id.size());
|
||||
bitStream.Write(id);
|
||||
|
||||
std::string toWrite;
|
||||
for (const auto* item : localizeParams) {
|
||||
toWrite += item->GetString() + "\n";
|
||||
}
|
||||
if (!toWrite.empty()) toWrite.pop_back();
|
||||
bitStream.Write<int32_t>(toWrite.size());
|
||||
bitStream.Write(GeneralUtils::ASCIIToUTF16(toWrite));
|
||||
if (!toWrite.empty()) bitStream.Write<uint16_t>(0x00); // Null Terminator
|
||||
|
||||
bitStream.Write<int32_t>(imageName.size());
|
||||
bitStream.Write(imageName);
|
||||
bitStream.Write<int32_t>(text.size());
|
||||
bitStream.Write(text);
|
||||
}
|
||||
|
||||
void UseItemOnClient::Serialize(RakNet::BitStream& bitStream) const {
|
||||
bitStream.Write(itemLOT);
|
||||
bitStream.Write(itemToUse);
|
||||
bitStream.Write(itemType);
|
||||
bitStream.Write(playerId);
|
||||
bitStream.Write(targetPosition.x);
|
||||
bitStream.Write(targetPosition.y);
|
||||
bitStream.Write(targetPosition.z);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include "eCyclingMode.h"
|
||||
#include "eLootSourceType.h"
|
||||
#include "Brick.h"
|
||||
#include "MessageType/Game.h"
|
||||
|
||||
class AMFBaseValue;
|
||||
class Entity;
|
||||
@@ -20,6 +21,7 @@ class User;
|
||||
class Leaderboard;
|
||||
class PropertySelectQueryProperty;
|
||||
class TradeItem;
|
||||
class LDFBaseData;
|
||||
|
||||
enum class eAnimationFlags : uint32_t;
|
||||
|
||||
@@ -48,6 +50,15 @@ enum class eCameraTargetCyclingMode : int32_t {
|
||||
};
|
||||
|
||||
namespace GameMessages {
|
||||
struct GameMsg {
|
||||
GameMsg(MessageType::Game gmId) : msgId{ gmId } {}
|
||||
virtual ~GameMsg() = default;
|
||||
void Send(const SystemAddress& sysAddr) const;
|
||||
virtual void Serialize(RakNet::BitStream& bitStream) const {}
|
||||
MessageType::Game msgId;
|
||||
LWOOBJID target{ LWOOBJID_EMPTY };
|
||||
};
|
||||
|
||||
class PropertyDataMessage;
|
||||
void SendFireEventClientSide(const LWOOBJID& objectID, const SystemAddress& sysAddr, std::u16string args, const LWOOBJID& object, int64_t param1, int param2, const LWOOBJID& sender);
|
||||
void SendTeleport(const LWOOBJID& objectID, const NiPoint3& pos, const NiQuaternion& rot, const SystemAddress& sysAddr, bool bSetRotation = false);
|
||||
@@ -700,6 +711,32 @@ namespace GameMessages {
|
||||
|
||||
// This is a client gm however its default values are exactly what we need to get around the invisible inventory item issues.
|
||||
void SendUpdateInventoryUi(LWOOBJID objectId, const SystemAddress& sysAddr);
|
||||
|
||||
struct DisplayTooltip : public GameMsg {
|
||||
DisplayTooltip() : GameMsg(MessageType::Game::DISPLAY_TOOLTIP) {}
|
||||
bool doOrDie{};
|
||||
bool noRepeat{};
|
||||
bool noRevive{};
|
||||
bool isPropertyTooltip{};
|
||||
bool show{};
|
||||
bool translate{};
|
||||
int32_t time{};
|
||||
std::u16string id{};
|
||||
std::vector<LDFBaseData*> localizeParams{};
|
||||
std::u16string imageName{};
|
||||
std::u16string text{};
|
||||
void Serialize(RakNet::BitStream& bitStream) const override;
|
||||
};
|
||||
|
||||
struct UseItemOnClient : public GameMsg {
|
||||
UseItemOnClient() : GameMsg(MessageType::Game::USE_ITEM_ON_CLIENT) {}
|
||||
LWOOBJID playerId{};
|
||||
LWOOBJID itemToUse{};
|
||||
uint32_t itemType{};
|
||||
LOT itemLOT{};
|
||||
NiPoint3 targetPosition{};
|
||||
void Serialize(RakNet::BitStream& bitStream) const override;
|
||||
};
|
||||
};
|
||||
|
||||
#endif // GAMEMESSAGES_H
|
||||
|
||||
@@ -8,7 +8,6 @@ class AMFArrayValue;
|
||||
/**
|
||||
* @brief Sent when a player moves a Behavior A at position B to their inventory.
|
||||
*/
|
||||
#pragma warning("This Control Behavior Message does not have a test yet. Non-developers can ignore this warning.")
|
||||
class MoveToInventoryMessage : public BehaviorMessageBase {
|
||||
public:
|
||||
MoveToInventoryMessage(const AMFArrayValue& arguments);
|
||||
|
||||
@@ -137,7 +137,7 @@ bool Precondition::CheckValue(Entity* player, const uint32_t value, bool evaluat
|
||||
|
||||
return inventoryComponent->GetLotCount(value) >= count;
|
||||
case PreconditionType::DoesNotHaveItem:
|
||||
return inventoryComponent->IsEquipped(value) < count;
|
||||
return inventoryComponent->IsEquipped(value) && count > 0;
|
||||
case PreconditionType::HasAchievement:
|
||||
if (missionComponent == nullptr) return false;
|
||||
return missionComponent->GetMissionState(value) >= eMissionState::COMPLETE;
|
||||
|
||||
@@ -287,8 +287,8 @@ void SlashCommandHandler::Startup() {
|
||||
RegisterCommand(SpawnPhysicsVertsCommand);
|
||||
|
||||
Command TeleportCommand{
|
||||
.help = "Teleports you",
|
||||
.info = "Teleports you. If no Y is given, you are teleported to the height of the terrain or physics object at (x, z)",
|
||||
.help = "Teleports you to a position or a player to another player.",
|
||||
.info = "Teleports you. If no Y is given, you are teleported to the height of the terrain or physics object at (x, z). Any of the coordinates can use the syntax of an exact position (10.0), or a relative position (~+10.0). A ~ means use the current value of that axis as the base value. Addition or subtraction is supported (~+10) (~-10). If source player and target player are players that exist in the world, then the source player will be teleported to target player.",
|
||||
.aliases = { "teleport", "tele", "tp" },
|
||||
.handle = DEVGMCommands::Teleport,
|
||||
.requiredLevel = eGameMasterLevel::JUNIOR_DEVELOPER
|
||||
@@ -1056,6 +1056,15 @@ void SlashCommandHandler::Startup() {
|
||||
};
|
||||
RegisterCommand(InstanceInfoCommand);
|
||||
|
||||
Command ServerUptimeCommand{
|
||||
.help = "Display the time the current world server has been active",
|
||||
.info = "Display the time the current world server has been active",
|
||||
.aliases = { "uptime" },
|
||||
.handle = GMZeroCommands::ServerUptime,
|
||||
.requiredLevel = eGameMasterLevel::DEVELOPER
|
||||
};
|
||||
RegisterCommand(ServerUptimeCommand);
|
||||
|
||||
//Commands that are handled by the client
|
||||
|
||||
Command faqCommand{
|
||||
|
||||
@@ -555,25 +555,45 @@ namespace DEVGMCommands {
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<float> ParseRelativeAxis(const float sourcePos, const std::string& toParse) {
|
||||
if (toParse.empty()) return std::nullopt;
|
||||
|
||||
// relative offset from current position
|
||||
if (toParse[0] == '~') {
|
||||
if (toParse.size() == 1) return sourcePos;
|
||||
|
||||
if (toParse.size() < 3 || !(toParse[1] != '+' || toParse[1] != '-')) return std::nullopt;
|
||||
|
||||
const auto offset = GeneralUtils::TryParse<float>(toParse.substr(2));
|
||||
if (!offset.has_value()) return std::nullopt;
|
||||
|
||||
bool isNegative = toParse[1] == '-';
|
||||
return isNegative ? sourcePos - offset.value() : sourcePos + offset.value();
|
||||
}
|
||||
|
||||
return GeneralUtils::TryParse<float>(toParse);
|
||||
}
|
||||
|
||||
void Teleport(Entity* entity, const SystemAddress& sysAddr, const std::string args) {
|
||||
const auto splitArgs = GeneralUtils::SplitString(args, ' ');
|
||||
|
||||
const auto& sourcePos = entity->GetPosition();
|
||||
NiPoint3 pos{};
|
||||
auto* sourceEntity = entity;
|
||||
if (splitArgs.size() == 3) {
|
||||
|
||||
const auto x = GeneralUtils::TryParse<float>(splitArgs.at(0));
|
||||
const auto x = ParseRelativeAxis(sourcePos.x, splitArgs[0]);
|
||||
if (!x) {
|
||||
ChatPackets::SendSystemMessage(sysAddr, u"Invalid x.");
|
||||
return;
|
||||
}
|
||||
|
||||
const auto y = GeneralUtils::TryParse<float>(splitArgs.at(1));
|
||||
const auto y = ParseRelativeAxis(sourcePos.y, splitArgs[1]);
|
||||
if (!y) {
|
||||
ChatPackets::SendSystemMessage(sysAddr, u"Invalid y.");
|
||||
return;
|
||||
}
|
||||
|
||||
const auto z = GeneralUtils::TryParse<float>(splitArgs.at(2));
|
||||
const auto z = ParseRelativeAxis(sourcePos.z, splitArgs[2]);
|
||||
if (!z) {
|
||||
ChatPackets::SendSystemMessage(sysAddr, u"Invalid z.");
|
||||
return;
|
||||
@@ -584,32 +604,39 @@ namespace DEVGMCommands {
|
||||
pos.SetZ(z.value());
|
||||
|
||||
LOG("Teleporting objectID: %llu to %f, %f, %f", entity->GetObjectID(), pos.x, pos.y, pos.z);
|
||||
GameMessages::SendTeleport(entity->GetObjectID(), pos, NiQuaternion(), sysAddr);
|
||||
} else if (splitArgs.size() == 2) {
|
||||
const auto x = ParseRelativeAxis(sourcePos.x, splitArgs[0]);
|
||||
auto* sourcePlayer = PlayerManager::GetPlayer(splitArgs[0]);
|
||||
if (!x && !sourcePlayer) {
|
||||
ChatPackets::SendSystemMessage(sysAddr, u"Invalid x or source player not found.");
|
||||
return;
|
||||
}
|
||||
if (sourcePlayer) sourceEntity = sourcePlayer;
|
||||
|
||||
const auto x = GeneralUtils::TryParse<float>(splitArgs.at(0));
|
||||
if (!x) {
|
||||
ChatPackets::SendSystemMessage(sysAddr, u"Invalid x.");
|
||||
const auto z = ParseRelativeAxis(sourcePos.z, splitArgs[1]);
|
||||
const auto* const targetPlayer = PlayerManager::GetPlayer(splitArgs[1]);
|
||||
if (!z && !targetPlayer) {
|
||||
ChatPackets::SendSystemMessage(sysAddr, u"Invalid z or target player not found.");
|
||||
return;
|
||||
}
|
||||
|
||||
const auto z = GeneralUtils::TryParse<float>(splitArgs.at(1));
|
||||
if (!z) {
|
||||
ChatPackets::SendSystemMessage(sysAddr, u"Invalid z.");
|
||||
if (x && z) {
|
||||
pos.SetX(x.value());
|
||||
pos.SetY(0.0f);
|
||||
pos.SetZ(z.value());
|
||||
} else if (sourcePlayer && targetPlayer) {
|
||||
pos = targetPlayer->GetPosition();
|
||||
} else {
|
||||
ChatPackets::SendSystemMessage(sysAddr, u"Unable to teleport.");
|
||||
return;
|
||||
}
|
||||
|
||||
pos.SetX(x.value());
|
||||
pos.SetY(0.0f);
|
||||
pos.SetZ(z.value());
|
||||
|
||||
LOG("Teleporting objectID: %llu to X: %f, Z: %f", entity->GetObjectID(), pos.x, pos.z);
|
||||
GameMessages::SendTeleport(entity->GetObjectID(), pos, NiQuaternion(), sysAddr);
|
||||
} else {
|
||||
ChatPackets::SendSystemMessage(sysAddr, u"Correct usage: /teleport <x> (<y>) <z> - if no Y given, will teleport to the height of the terrain (or any physics object).");
|
||||
}
|
||||
GameMessages::SendTeleport(sourceEntity->GetObjectID(), pos, sourceEntity->GetRotation(), sourceEntity->GetSystemAddress());
|
||||
|
||||
auto* possessorComponent = entity->GetComponent<PossessorComponent>();
|
||||
auto* possessorComponent = sourceEntity->GetComponent<PossessorComponent>();
|
||||
if (possessorComponent) {
|
||||
auto* possassableEntity = Game::entityManager->GetEntity(possessorComponent->GetPossessable());
|
||||
|
||||
|
||||
@@ -225,8 +225,13 @@ namespace GMZeroCommands {
|
||||
ChatPackets::SendSystemMessage(sysAddr, u"Map: " + (GeneralUtils::to_u16string(zoneId.GetMapID())) + u"\nClone: " + (GeneralUtils::to_u16string(zoneId.GetCloneID())) + u"\nInstance: " + (GeneralUtils::to_u16string(zoneId.GetInstanceID())));
|
||||
}
|
||||
|
||||
// Display the server uptime
|
||||
void ServerUptime(Entity* entity, const SystemAddress& sysAddr, const std::string args) {
|
||||
const auto time = Game::server->GetUptime();
|
||||
const auto seconds = std::chrono::duration_cast<std::chrono::seconds>(time).count();
|
||||
ChatPackets::SendSystemMessage(sysAddr, u"Server has been up for " + GeneralUtils::to_u16string(seconds) + u" s");
|
||||
}
|
||||
|
||||
//For client side commands
|
||||
void ClientHandled(Entity* entity, const SystemAddress& sysAddr, const std::string args) {}
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@ namespace GMZeroCommands {
|
||||
void LeaveZone(Entity* entity, const SystemAddress& sysAddr, const std::string args);
|
||||
void Resurrect(Entity* entity, const SystemAddress& sysAddr, const std::string args);
|
||||
void InstanceInfo(Entity* entity, const SystemAddress& sysAddr, const std::string args);
|
||||
void ServerUptime(Entity* entity, const SystemAddress& sysAddr, const std::string args);
|
||||
void ClientHandled(Entity* entity, const SystemAddress& sysAddr, const std::string args);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user