mirror of
https://github.com/DarkflameUniverse/DarkflameServer.git
synced 2025-05-23 23:32:29 +00:00
406 lines
12 KiB
C++
406 lines
12 KiB
C++
#include "ControllablePhysicsComponent.h"
|
|
#include "Entity.h"
|
|
#include "BitStream.h"
|
|
#include "dLogger.h"
|
|
#include "Game.h"
|
|
|
|
#include "dpWorld.h"
|
|
#include "dpEntity.h"
|
|
#include "CDPhysicsComponentTable.h"
|
|
#include "CDComponentsRegistryTable.h"
|
|
#include "CDClientManager.h"
|
|
#include "EntityManager.h"
|
|
#include "Character.h"
|
|
#include "dZoneManager.h"
|
|
|
|
ControllablePhysicsComponent::ControllablePhysicsComponent(Entity* entity) : Component(entity) {
|
|
m_Position = {};
|
|
m_Rotation = NiQuaternion::IDENTITY;
|
|
m_Velocity = {};
|
|
m_AngularVelocity = {};
|
|
m_InJetpackMode = false;
|
|
m_IsOnGround = true;
|
|
m_IsOnRail = false;
|
|
m_DirtyPosition = true;
|
|
m_DirtyVelocity = true;
|
|
m_DirtyAngularVelocity = true;
|
|
m_dpEntity = nullptr;
|
|
m_Static = false;
|
|
m_SpeedMultiplier = 1;
|
|
m_GravityScale = 1;
|
|
m_DirtyCheats = false;
|
|
m_IgnoreMultipliers = false;
|
|
m_PickupRadius = 0.0f;
|
|
m_DirtyPickupRadiusScale = true;
|
|
m_IsTeleporting = false;
|
|
m_AttachedPath = entity->GetVarAsString(u"attached_path");
|
|
m_PathWaypoint = entity->GetVarAs<int>(u"attached_path_start");
|
|
m_PathSpeed = 2.5f;
|
|
m_BaseSpeed = GetBaseSpeed(m_Parent->GetLOT());
|
|
|
|
|
|
if (entity->GetLOT() != 1) // Other physics entities we care about will be added by BaseCombatAI
|
|
return;
|
|
|
|
if (entity->GetLOT() == 1) {
|
|
Game::logger->Log("ControllablePhysicsComponent", "Using patch to load minifig physics");
|
|
|
|
float radius = 1.5f;
|
|
m_dpEntity = new dpEntity(m_Parent->GetObjectID(), radius, false);
|
|
m_dpEntity->SetCollisionGroup(COLLISION_GROUP_DYNAMIC | COLLISION_GROUP_FRIENDLY);
|
|
dpWorld::Instance().AddEntity(m_dpEntity);
|
|
}
|
|
}
|
|
|
|
ControllablePhysicsComponent::~ControllablePhysicsComponent() {
|
|
if (m_dpEntity) {
|
|
dpWorld::Instance().RemoveEntity(m_dpEntity);
|
|
}
|
|
}
|
|
|
|
void ControllablePhysicsComponent::Update(float deltaTime) {
|
|
if (m_AttachedPath != ""){
|
|
const auto* path = dZoneManager::Instance()->GetZone()->GetPath(m_AttachedPath);
|
|
|
|
if (!m_Paused){
|
|
if (path->pathWaypoints.size() > m_PathWaypoint) {
|
|
auto mod_speed = m_BaseSpeed;
|
|
if (m_PathSpeed < 1.0) {
|
|
mod_speed = m_BaseSpeed * m_PathSpeed;
|
|
} else {
|
|
mod_speed = m_PathSpeed;
|
|
}
|
|
|
|
auto speed = deltaTime * mod_speed;
|
|
auto source = GetPosition();
|
|
auto dest = path->pathWaypoints.at(m_PathWaypoint).position;
|
|
dest.y = source.y; // hacky way to not glitch with weird heights
|
|
|
|
// if we are close enough to the destination waypoint
|
|
if (Vector3::DistanceSquared(source, dest) < 2 * 2) {
|
|
if (path->pathBehavior == PathBehavior::Loop) {
|
|
if (path->waypointCount < m_PathWaypoint + 1) {
|
|
m_PathWaypoint = 0;
|
|
} else m_PathWaypoint++;
|
|
} else if (path->pathBehavior == PathBehavior::Once){
|
|
if (path->waypointCount < m_PathWaypoint + 1) {
|
|
m_AttachedPath = "";
|
|
} else m_PathWaypoint++;
|
|
}
|
|
m_Paused = true;
|
|
return;
|
|
}
|
|
|
|
const auto delta = dest - source;
|
|
const auto length = sqrtf(delta.x * delta.x + delta.y * delta.y + delta.z * delta.z);
|
|
NiPoint3 velocity;
|
|
NiPoint3 velocity_pos;
|
|
if (length > 0) {
|
|
velocity_pos.x = (delta.x / length) * speed;
|
|
velocity_pos.y = (delta.y / length) * speed;
|
|
velocity_pos.z = (delta.z / length) * speed;
|
|
}
|
|
speed = speed + 2.8f;
|
|
if (length > 0) {
|
|
velocity.x = (delta.x / length) * speed;
|
|
velocity.y = (delta.y / length) * speed;
|
|
velocity.z = (delta.z / length) * speed;
|
|
}
|
|
SetRotation(NiQuaternion::LookAt(source, dest));
|
|
SetVelocity(velocity);
|
|
SetPosition(source + velocity_pos);
|
|
EntityManager::Instance()->SerializeEntity(m_Parent);
|
|
} else if (path->pathBehavior == PathBehavior::Loop) m_PathWaypoint = 0;
|
|
|
|
} else { // paused, meaing we are at a waypoint, and we want to do something
|
|
if (m_PausedTime > 0) {
|
|
m_PausedTime = m_PausedTime - deltaTime;
|
|
} else if (m_PausedTime < 0){
|
|
m_Paused = false;
|
|
m_PausedTime = 0;
|
|
} else if (path->pathWaypoints.size() > m_PathWaypoint) {
|
|
PathWaypoint waypoint = path->pathWaypoints.at(m_PathWaypoint);
|
|
if (waypoint.config.size() > 0) {
|
|
for (LDFBaseData* action : waypoint.config) {
|
|
if (action) {
|
|
if (action->GetKey() == u"delay"){
|
|
m_PausedTime = std::stof(action->GetValueAsString());
|
|
SetVelocity(NiPoint3::ZERO);
|
|
EntityManager::Instance()->SerializeEntity(m_Parent);
|
|
} else if (action->GetKey() == u"emote"){
|
|
GameMessages::SendPlayAnimation(m_Parent, GeneralUtils::UTF8ToUTF16(action->GetValueAsString()));
|
|
m_PausedTime += 10;
|
|
SetVelocity(NiPoint3::ZERO);
|
|
EntityManager::Instance()->SerializeEntity(m_Parent);
|
|
} else if (action->GetKey() == u"pathspeed") {
|
|
m_PathSpeed = std::stof(action->GetValueAsString());
|
|
if (m_PathSpeed < 2.5f) m_PathSpeed = 2.5f;
|
|
} else if (action->GetKey() == u"changeWP") {
|
|
m_AttachedPath = action->GetValueAsString();
|
|
} else {
|
|
Game::logger->LogDebug("ControllablePhysicsComponent", "Unhandled action %s", GeneralUtils::UTF16ToWTF8(action->GetKey()).c_str());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (m_PausedTime == 0) {
|
|
m_Paused = false;
|
|
}
|
|
} else if (path->pathBehavior == PathBehavior::Loop) m_PathWaypoint = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
void ControllablePhysicsComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags) {
|
|
//If this is a creation, then we assume the position is dirty, even when it isn't.
|
|
//This is because new clients will still need to receive the position.
|
|
//if (bIsInitialUpdate) m_DirtyPosition = true;
|
|
|
|
if (bIsInitialUpdate) {
|
|
outBitStream->Write(m_InJetpackMode);
|
|
if (m_InJetpackMode) {
|
|
outBitStream->Write(m_JetpackEffectID);
|
|
outBitStream->Write(m_JetpackFlying);
|
|
outBitStream->Write(m_JetpackBypassChecks);
|
|
}
|
|
|
|
outBitStream->Write0(); //This contains info about immunities, but for now I'm leaving it out.
|
|
}
|
|
|
|
if (m_SpeedMultiplier < 1.0f) {
|
|
m_DirtyCheats = false;
|
|
}
|
|
|
|
if (m_IgnoreMultipliers) {
|
|
m_DirtyCheats = false;
|
|
}
|
|
|
|
outBitStream->Write(m_DirtyCheats);
|
|
if (m_DirtyCheats) {
|
|
outBitStream->Write(m_GravityScale);
|
|
outBitStream->Write(m_SpeedMultiplier);
|
|
|
|
m_DirtyCheats = false;
|
|
}
|
|
|
|
outBitStream->Write(m_DirtyPickupRadiusScale);
|
|
if (m_DirtyPickupRadiusScale) {
|
|
outBitStream->Write(m_PickupRadius);
|
|
outBitStream->Write0(); //No clue what this is so im leaving it false.
|
|
m_DirtyPickupRadiusScale = false;
|
|
}
|
|
|
|
outBitStream->Write0();
|
|
|
|
outBitStream->Write(m_DirtyPosition || bIsInitialUpdate);
|
|
if (m_DirtyPosition || bIsInitialUpdate) {
|
|
outBitStream->Write(m_Position.x);
|
|
outBitStream->Write(m_Position.y);
|
|
outBitStream->Write(m_Position.z);
|
|
|
|
outBitStream->Write(m_Rotation.x);
|
|
outBitStream->Write(m_Rotation.y);
|
|
outBitStream->Write(m_Rotation.z);
|
|
outBitStream->Write(m_Rotation.w);
|
|
|
|
outBitStream->Write(m_IsOnGround);
|
|
outBitStream->Write(m_IsOnRail);
|
|
|
|
outBitStream->Write(m_DirtyVelocity);
|
|
if (m_DirtyVelocity) {
|
|
outBitStream->Write(m_Velocity.x);
|
|
outBitStream->Write(m_Velocity.y);
|
|
outBitStream->Write(m_Velocity.z);
|
|
}
|
|
|
|
outBitStream->Write(m_DirtyAngularVelocity);
|
|
if (m_DirtyAngularVelocity) {
|
|
outBitStream->Write(m_AngularVelocity.x);
|
|
outBitStream->Write(m_AngularVelocity.y);
|
|
outBitStream->Write(m_AngularVelocity.z);
|
|
}
|
|
|
|
outBitStream->Write0();
|
|
}
|
|
|
|
if (!bIsInitialUpdate) {
|
|
outBitStream->Write(m_IsTeleporting);
|
|
m_IsTeleporting = false;
|
|
}
|
|
}
|
|
|
|
void ControllablePhysicsComponent::LoadFromXml(tinyxml2::XMLDocument* doc) {
|
|
tinyxml2::XMLElement* character = doc->FirstChildElement("obj")->FirstChildElement("char");
|
|
if (!character) {
|
|
Game::logger->Log("ControllablePhysicsComponent", "Failed to find char tag!");
|
|
return;
|
|
}
|
|
|
|
m_Parent->GetCharacter()->LoadXmlRespawnCheckpoints();
|
|
|
|
character->QueryAttribute("lzx", &m_Position.x);
|
|
character->QueryAttribute("lzy", &m_Position.y);
|
|
character->QueryAttribute("lzz", &m_Position.z);
|
|
character->QueryAttribute("lzrx", &m_Rotation.x);
|
|
character->QueryAttribute("lzry", &m_Rotation.y);
|
|
character->QueryAttribute("lzrz", &m_Rotation.z);
|
|
character->QueryAttribute("lzrw", &m_Rotation.w);
|
|
|
|
m_DirtyPosition = true;
|
|
}
|
|
|
|
void ControllablePhysicsComponent::ResetFlags() {
|
|
m_DirtyAngularVelocity = false;
|
|
m_DirtyPosition = false;
|
|
m_DirtyVelocity = false;
|
|
}
|
|
|
|
void ControllablePhysicsComponent::UpdateXml(tinyxml2::XMLDocument* doc) {
|
|
tinyxml2::XMLElement* character = doc->FirstChildElement("obj")->FirstChildElement("char");
|
|
if (!character) {
|
|
Game::logger->Log("ControllablePhysicsComponent", "Failed to find char tag while updating XML!");
|
|
return;
|
|
}
|
|
|
|
auto zoneInfo = dZoneManager::Instance()->GetZone()->GetZoneID();
|
|
|
|
if (zoneInfo.GetMapID() != 0 && zoneInfo.GetCloneID() == 0) {
|
|
character->SetAttribute("lzx", m_Position.x);
|
|
character->SetAttribute("lzy", m_Position.y);
|
|
character->SetAttribute("lzz", m_Position.z);
|
|
character->SetAttribute("lzrx", m_Rotation.x);
|
|
character->SetAttribute("lzry", m_Rotation.y);
|
|
character->SetAttribute("lzrz", m_Rotation.z);
|
|
character->SetAttribute("lzrw", m_Rotation.w);
|
|
}
|
|
}
|
|
|
|
void ControllablePhysicsComponent::SetPosition(const NiPoint3& pos) {
|
|
if (m_Static) {
|
|
return;
|
|
}
|
|
|
|
m_Position.x = pos.x;
|
|
m_Position.y = pos.y;
|
|
m_Position.z = pos.z;
|
|
m_DirtyPosition = true;
|
|
|
|
if (m_dpEntity) m_dpEntity->SetPosition(pos);
|
|
}
|
|
|
|
void ControllablePhysicsComponent::SetRotation(const NiQuaternion& rot) {
|
|
if (m_Static) {
|
|
return;
|
|
}
|
|
|
|
m_Rotation = rot;
|
|
m_DirtyPosition = true;
|
|
|
|
if (m_dpEntity) m_dpEntity->SetRotation(rot);
|
|
}
|
|
|
|
void ControllablePhysicsComponent::SetVelocity(const NiPoint3& vel) {
|
|
if (m_Static) {
|
|
return;
|
|
}
|
|
|
|
m_Velocity = vel;
|
|
m_DirtyPosition = true;
|
|
m_DirtyVelocity = true;
|
|
|
|
if (m_dpEntity) m_dpEntity->SetVelocity(vel);
|
|
}
|
|
|
|
void ControllablePhysicsComponent::SetAngularVelocity(const NiPoint3& vel) {
|
|
if (m_Static) {
|
|
return;
|
|
}
|
|
|
|
m_AngularVelocity = vel;
|
|
m_DirtyPosition = true;
|
|
m_DirtyAngularVelocity = true;
|
|
}
|
|
|
|
void ControllablePhysicsComponent::SetIsOnGround(bool val) {
|
|
m_DirtyPosition = true;
|
|
m_IsOnGround = val;
|
|
}
|
|
|
|
void ControllablePhysicsComponent::SetIsOnRail(bool val) {
|
|
m_DirtyPosition = true;
|
|
m_IsOnRail = val;
|
|
}
|
|
|
|
void ControllablePhysicsComponent::SetDirtyPosition(bool val) {
|
|
m_DirtyPosition = val;
|
|
}
|
|
|
|
void ControllablePhysicsComponent::SetDirtyVelocity(bool val) {
|
|
m_DirtyVelocity = val;
|
|
}
|
|
|
|
void ControllablePhysicsComponent::SetDirtyAngularVelocity(bool val) {
|
|
m_DirtyAngularVelocity = val;
|
|
}
|
|
|
|
void ControllablePhysicsComponent::AddPickupRadiusScale(float value) {
|
|
m_ActivePickupRadiusScales.push_back(value);
|
|
if (value > m_PickupRadius) {
|
|
m_PickupRadius = value;
|
|
m_DirtyPickupRadiusScale = true;
|
|
}
|
|
}
|
|
|
|
void ControllablePhysicsComponent::RemovePickupRadiusScale(float value) {
|
|
// Attempt to remove pickup radius from active radii
|
|
const auto pos = std::find(m_ActivePickupRadiusScales.begin(), m_ActivePickupRadiusScales.end(), value);
|
|
if (pos != m_ActivePickupRadiusScales.end()) {
|
|
m_ActivePickupRadiusScales.erase(pos);
|
|
} else {
|
|
Game::logger->Log("ControllablePhysicsComponent", "Warning: Could not find pickup radius %f in list of active radii. List has %i active radii.", value, m_ActivePickupRadiusScales.size());
|
|
return;
|
|
}
|
|
|
|
// Recalculate pickup radius since we removed one by now
|
|
m_PickupRadius = 0.0f;
|
|
m_DirtyPickupRadiusScale = true;
|
|
for (uint32_t i = 0; i < m_ActivePickupRadiusScales.size(); i++) {
|
|
auto candidateRadius = m_ActivePickupRadiusScales[i];
|
|
if (m_PickupRadius < candidateRadius) m_PickupRadius = candidateRadius;
|
|
}
|
|
EntityManager::Instance()->SerializeEntity(m_Parent);
|
|
}
|
|
|
|
// void ControllablePhysicsComponent::FollowWaypoints(bool paused, std::string newPathName, int newPathStart = 0){
|
|
// m_Paused = paused;
|
|
// m_AttachedPath = newPathName;
|
|
// m_PathWaypoint = newPathStart;
|
|
// }
|
|
|
|
// void ControllablePhysicsComponent::FollowWaypoints(std::string newPathName, int newPathStart = 0){
|
|
// m_AttachedPath = newPathName;
|
|
// m_PathWaypoint = newPathStart;
|
|
// }
|
|
|
|
float ControllablePhysicsComponent::GetBaseSpeed(LOT lot) {
|
|
|
|
CDComponentsRegistryTable* componentRegistryTable = CDClientManager::Instance()->GetTable<CDComponentsRegistryTable>("ComponentsRegistry");
|
|
CDPhysicsComponentTable* physicsComponentTable = CDClientManager::Instance()->GetTable<CDPhysicsComponentTable>("PhysicsComponent");
|
|
|
|
int32_t componentID;
|
|
CDPhysicsComponent* physicsComponent = nullptr;
|
|
|
|
componentID = componentRegistryTable->GetByIDAndType(lot, COMPONENT_TYPE_CONTROLLABLE_PHYSICS, -1);
|
|
|
|
if (componentID != -1) physicsComponent = physicsComponentTable->GetByID(componentID);
|
|
|
|
float speed;
|
|
if (physicsComponent == nullptr) {
|
|
speed = 8;
|
|
} else {
|
|
speed = physicsComponent->speed;
|
|
}
|
|
|
|
return speed;
|
|
}
|