diff --git a/dGame/Entity.cpp b/dGame/Entity.cpp index 6de6ba62..bac07713 100644 --- a/dGame/Entity.cpp +++ b/dGame/Entity.cpp @@ -386,8 +386,8 @@ void Entity::Initialize() { comp->SetMaxCoins(currencyValues[0].maxvalue); } - // extraInfo overrides - comp->SetIsSmashable(GetVarAs(u"is_smashable") != 0); + // extraInfo overrides. Client ORs the database smashable and the luz smashable. + comp->SetIsSmashable(comp->GetIsSmashable() | (GetVarAs(u"is_smashable") != 0)); } } else { comp->SetHealth(1); @@ -1827,8 +1827,6 @@ bool Entity::IsSleeping() const { const NiPoint3& Entity::GetPosition() const { - if (!this) return NiPoint3::ZERO; - auto* controllable = GetComponent(); if (controllable != nullptr) { diff --git a/dGame/dComponents/DestroyableComponent.cpp b/dGame/dComponents/DestroyableComponent.cpp index 01ebf5c1..db8a2013 100644 --- a/dGame/dComponents/DestroyableComponent.cpp +++ b/dGame/dComponents/DestroyableComponent.cpp @@ -51,7 +51,7 @@ DestroyableComponent::DestroyableComponent(Entity* parent) : Component(parent) { m_IsGMImmune = false; m_IsShielded = false; m_DamageToAbsorb = 0; - m_HasBricks = false; + m_IsModuleAssembly = m_Parent->HasComponent(eReplicaComponentType::MODULE_ASSEMBLY); m_DirtyThreatList = false; m_HasThreats = false; m_ExplodeFactor = 1.0f; @@ -163,7 +163,7 @@ void DestroyableComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsIn outBitStream->Write(m_IsSmashed); if (m_IsSmashable) { - outBitStream->Write(m_HasBricks); + outBitStream->Write(m_IsModuleAssembly); outBitStream->Write(m_ExplodeFactor != 1.0f); if (m_ExplodeFactor != 1.0f) outBitStream->Write(m_ExplodeFactor); } diff --git a/dGame/dComponents/DestroyableComponent.h b/dGame/dComponents/DestroyableComponent.h index 66c8374d..5e5133b7 100644 --- a/dGame/dComponents/DestroyableComponent.h +++ b/dGame/dComponents/DestroyableComponent.h @@ -239,7 +239,7 @@ public: * Returns whether or not this entity has bricks flying out when smashed * @return whether or not this entity has bricks flying out when smashed */ - bool GetHasBricks() const { return m_HasBricks; } + bool GetHasBricks() const { return m_IsModuleAssembly; } /** * Sets the multiplier for the explosion that's visible when the bricks fly out when this entity is smashed @@ -546,7 +546,7 @@ private: /** * Whether this entity has bricks flying out when smashed (causes the client to look up the files) */ - bool m_HasBricks; + bool m_IsModuleAssembly; /** * The rate at which bricks fly out when smashed diff --git a/dGame/dComponents/InventoryComponent.cpp b/dGame/dComponents/InventoryComponent.cpp index 907356ce..626c7ee9 100644 --- a/dGame/dComponents/InventoryComponent.cpp +++ b/dGame/dComponents/InventoryComponent.cpp @@ -38,7 +38,7 @@ #include "CDObjectSkillsTable.h" #include "CDSkillBehaviorTable.h" -InventoryComponent::InventoryComponent(Entity* parent, tinyxml2::XMLDocument* document): Component(parent) { +InventoryComponent::InventoryComponent(Entity* parent, tinyxml2::XMLDocument* document) : Component(parent) { this->m_Dirty = true; this->m_Equipped = {}; this->m_Pushed = {}; @@ -830,14 +830,22 @@ void InventoryComponent::EquipItem(Item* item, const bool skipChecks) { const auto position = m_Parent->GetPosition(); - for (auto* lauchPad : rocketLauchPads) { - if (Vector3::DistanceSquared(lauchPad->GetPosition(), position) > 13 * 13) continue; + for (auto* launchPad : rocketLauchPads) { + if (!launchPad) continue; + + auto prereq = launchPad->GetVarAsString(u"rocketLaunchPreCondition"); + if (!prereq.empty()) { + PreconditionExpression expression(prereq); + if (!expression.Check(m_Parent)) continue; + } + + if (Vector3::DistanceSquared(launchPad->GetPosition(), position) > 13 * 13) continue; auto* characterComponent = m_Parent->GetComponent(); if (characterComponent != nullptr) characterComponent->SetLastRocketItemID(item->GetId()); - lauchPad->OnUse(m_Parent); + launchPad->OnUse(m_Parent); break; } @@ -1002,7 +1010,6 @@ void InventoryComponent::HandlePossession(Item* item) { // Setup the destroyable stats auto* destroyableComponent = mount->GetComponent(); if (destroyableComponent) { - destroyableComponent->SetIsSmashable(false); destroyableComponent->SetIsImmune(true); } diff --git a/dGame/dComponents/RacingControlComponent.cpp b/dGame/dComponents/RacingControlComponent.cpp index dd7921db..d76901b3 100644 --- a/dGame/dComponents/RacingControlComponent.cpp +++ b/dGame/dComponents/RacingControlComponent.cpp @@ -125,8 +125,7 @@ void RacingControlComponent::LoadPlayerVehicle(Entity* player, // Make sure the player is at the correct position. GameMessages::SendTeleport(player->GetObjectID(), startPosition, - startRotation, player->GetSystemAddress(), true, - true); + startRotation, player->GetSystemAddress(), true); // Spawn the vehicle entity. @@ -244,11 +243,9 @@ void RacingControlComponent::LoadPlayerVehicle(Entity* player, // Make sure everything has the correct position. GameMessages::SendTeleport(player->GetObjectID(), startPosition, - startRotation, player->GetSystemAddress(), true, - true); + startRotation, player->GetSystemAddress(), true); GameMessages::SendTeleport(carEntity->GetObjectID(), startPosition, - startRotation, player->GetSystemAddress(), true, - true); + startRotation, player->GetSystemAddress(), true); } void RacingControlComponent::OnRacingClientReady(Entity* player) { @@ -633,7 +630,7 @@ void RacingControlComponent::Update(float deltaTime) { GameMessages::SendTeleport( player.playerID, player.respawnPosition, player.respawnRotation, - playerEntity->GetSystemAddress(), true, true); + playerEntity->GetSystemAddress(), true); vehicle->SetPosition(player.respawnPosition); vehicle->SetRotation(player.respawnRotation); diff --git a/dGame/dComponents/VehiclePhysicsComponent.cpp b/dGame/dComponents/VehiclePhysicsComponent.cpp index d981acf7..58bf7ebb 100644 --- a/dGame/dComponents/VehiclePhysicsComponent.cpp +++ b/dGame/dComponents/VehiclePhysicsComponent.cpp @@ -19,34 +19,47 @@ VehiclePhysicsComponent::~VehiclePhysicsComponent() { } void VehiclePhysicsComponent::SetPosition(const NiPoint3& pos) { + if (pos == m_Position) return; + m_DirtyPosition = true; m_Position = pos; } void VehiclePhysicsComponent::SetRotation(const NiQuaternion& rot) { + if (rot == m_Rotation) return; m_DirtyPosition = true; m_Rotation = rot; } void VehiclePhysicsComponent::SetVelocity(const NiPoint3& vel) { + if (vel == m_Velocity) return; m_DirtyPosition = true; m_Velocity = vel; } void VehiclePhysicsComponent::SetAngularVelocity(const NiPoint3& vel) { + if (vel == m_AngularVelocity) return; m_DirtyPosition = true; m_AngularVelocity = vel; } void VehiclePhysicsComponent::SetIsOnGround(bool val) { + if (val == m_IsOnGround) return; m_DirtyPosition = true; m_IsOnGround = val; } void VehiclePhysicsComponent::SetIsOnRail(bool val) { + if (val == m_IsOnRail) return; m_DirtyPosition = true; m_IsOnRail = val; } +void VehiclePhysicsComponent::SetRemoteInputInfo(const RemoteInputInfo& remoteInputInfo) { + if (m_RemoteInputInfo == remoteInputInfo) return; + this->m_RemoteInputInfo = remoteInputInfo; + m_DirtyRemoteInput = true; +} + void VehiclePhysicsComponent::SetDirtyPosition(bool val) { m_DirtyPosition = val; } @@ -63,9 +76,15 @@ void VehiclePhysicsComponent::Serialize(RakNet::BitStream* outBitStream, bool bI outBitStream->Write(bIsInitialUpdate || m_DirtyPosition); if (bIsInitialUpdate || m_DirtyPosition) { - outBitStream->Write(m_Position); + m_DirtyPosition = false; + outBitStream->Write(m_Position.x); + outBitStream->Write(m_Position.y); + outBitStream->Write(m_Position.z); - outBitStream->Write(m_Rotation); + 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); @@ -73,20 +92,33 @@ void VehiclePhysicsComponent::Serialize(RakNet::BitStream* outBitStream, bool bI outBitStream->Write(bIsInitialUpdate || m_DirtyVelocity); if (bIsInitialUpdate || m_DirtyVelocity) { - outBitStream->Write(m_Velocity); + outBitStream->Write(m_Velocity.x); + outBitStream->Write(m_Velocity.y); + outBitStream->Write(m_Velocity.z); + m_DirtyVelocity = false; } outBitStream->Write(bIsInitialUpdate || m_DirtyAngularVelocity); if (bIsInitialUpdate || m_DirtyAngularVelocity) { - outBitStream->Write(m_AngularVelocity); + outBitStream->Write(m_AngularVelocity.x); + outBitStream->Write(m_AngularVelocity.y); + outBitStream->Write(m_AngularVelocity.z); + m_DirtyAngularVelocity = false; } - outBitStream->Write0(); + outBitStream->Write0(); // local_space_info. TODO: Implement this - outBitStream->Write0(); + outBitStream->Write(m_DirtyRemoteInput || bIsInitialUpdate); // remote_input_info + if (m_DirtyRemoteInput || bIsInitialUpdate) { + outBitStream->Write(m_RemoteInputInfo.m_RemoteInputX); + outBitStream->Write(m_RemoteInputInfo.m_RemoteInputY); + outBitStream->Write(m_RemoteInputInfo.m_IsPowersliding); + outBitStream->Write(m_RemoteInputInfo.m_IsModified); + m_DirtyRemoteInput = false; + } - outBitStream->Write(0.0f); + outBitStream->Write(125.0f); // remote_input_ping TODO: Figure out how this should be calculated as it seems to be constant through the whole race. if (!bIsInitialUpdate) { outBitStream->Write0(); @@ -95,7 +127,7 @@ void VehiclePhysicsComponent::Serialize(RakNet::BitStream* outBitStream, bool bI if (bIsInitialUpdate) { outBitStream->Write(m_EndBehavior); - outBitStream->Write1(); + outBitStream->Write1(); // is input locked? } outBitStream->Write0(); @@ -104,7 +136,6 @@ void VehiclePhysicsComponent::Serialize(RakNet::BitStream* outBitStream, bool bI void VehiclePhysicsComponent::Update(float deltaTime) { if (m_SoftUpdate > 5) { EntityManager::Instance()->SerializeEntity(m_Parent); - m_SoftUpdate = 0; } else { m_SoftUpdate += deltaTime; diff --git a/dGame/dComponents/VehiclePhysicsComponent.h b/dGame/dComponents/VehiclePhysicsComponent.h index 64609edf..e314bef1 100644 --- a/dGame/dComponents/VehiclePhysicsComponent.h +++ b/dGame/dComponents/VehiclePhysicsComponent.h @@ -5,6 +5,24 @@ #include "Component.h" #include "eReplicaComponentType.h" +struct RemoteInputInfo { + void operator=(const RemoteInputInfo& other) { + m_RemoteInputX = other.m_RemoteInputX; + m_RemoteInputY = other.m_RemoteInputY; + m_IsPowersliding = other.m_IsPowersliding; + m_IsModified = other.m_IsModified; + } + + bool operator==(const RemoteInputInfo& other) { + return m_RemoteInputX == other.m_RemoteInputX && m_RemoteInputY == other.m_RemoteInputY && m_IsPowersliding == other.m_IsPowersliding && m_IsModified == other.m_IsModified; + } + + float m_RemoteInputX; + float m_RemoteInputY; + bool m_IsPowersliding; + bool m_IsModified; +}; + /** * Physics component for vehicles. */ @@ -94,6 +112,7 @@ public: void SetDirtyPosition(bool val); void SetDirtyVelocity(bool val); void SetDirtyAngularVelocity(bool val); + void SetRemoteInputInfo(const RemoteInputInfo&); private: bool m_DirtyPosition; @@ -110,4 +129,6 @@ private: float m_SoftUpdate = 0; uint32_t m_EndBehavior; + RemoteInputInfo m_RemoteInputInfo; + bool m_DirtyRemoteInput; }; diff --git a/dGame/dGameMessages/GameMessages.cpp b/dGame/dGameMessages/GameMessages.cpp index 198ea151..f1f345ea 100644 --- a/dGame/dGameMessages/GameMessages.cpp +++ b/dGame/dGameMessages/GameMessages.cpp @@ -120,7 +120,7 @@ void GameMessages::SendFireEventClientSide(const LWOOBJID& objectID, const Syste SEND_PACKET; } -void GameMessages::SendTeleport(const LWOOBJID& objectID, const NiPoint3& pos, const NiQuaternion& rot, const SystemAddress& sysAddr, bool bSetRotation, bool noGravTeleport) { +void GameMessages::SendTeleport(const LWOOBJID& objectID, const NiPoint3& pos, const NiQuaternion& rot, const SystemAddress& sysAddr, bool bSetRotation) { CBITSTREAM; CMSGHEADER; bitStream.Write(objectID); @@ -141,7 +141,6 @@ void GameMessages::SendTeleport(const LWOOBJID& objectID, const NiPoint3& pos, c bitStream.Write(pos.y); bitStream.Write(pos.z); bitStream.Write(bUseNavmesh); - bitStream.Write(noGravTeleport); bitStream.Write(rot.w != 1.0f); if (rot.w != 1.0f) bitStream.Write(rot.w); diff --git a/dGame/dGameMessages/GameMessages.h b/dGame/dGameMessages/GameMessages.h index 7e7a8f70..94bdd3ea 100644 --- a/dGame/dGameMessages/GameMessages.h +++ b/dGame/dGameMessages/GameMessages.h @@ -40,7 +40,7 @@ enum class eRebuildState : uint32_t; namespace GameMessages { 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, bool noGravTeleport = true); + void SendTeleport(const LWOOBJID& objectID, const NiPoint3& pos, const NiQuaternion& rot, const SystemAddress& sysAddr, bool bSetRotation = false); void SendPlayAnimation(Entity* entity, const std::u16string& animationName, float fPriority = 0.0f, float fScale = 1.0f); void SendPlayerReady(Entity* entity, const SystemAddress& sysAddr); void SendPlayerAllowedRespawn(LWOOBJID entityID, bool doNotPromptRespawn, const SystemAddress& systemAddress); diff --git a/dGame/dUtilities/VanityUtilities.cpp b/dGame/dUtilities/VanityUtilities.cpp index 7fabfbce..703f7d30 100644 --- a/dGame/dUtilities/VanityUtilities.cpp +++ b/dGame/dUtilities/VanityUtilities.cpp @@ -119,7 +119,7 @@ void VanityUtilities::SpawnVanity() { auto* scriptComponent = npcEntity->GetComponent(); - if (scriptComponent != nullptr) { + if (scriptComponent && !npc.m_Script.empty()) { scriptComponent->SetScript(npc.m_Script); scriptComponent->SetSerialized(false); @@ -163,7 +163,7 @@ Entity* VanityUtilities::SpawnNPC(LOT lot, const std::string& name, const NiPoin auto* inventoryComponent = entity->GetComponent(); - if (inventoryComponent != nullptr) { + if (inventoryComponent && !inventory.empty()) { inventoryComponent->SetNPCItems(inventory); } diff --git a/dNet/ClientPackets.cpp b/dNet/ClientPackets.cpp index 1c22bdcd..313be6b0 100644 --- a/dNet/ClientPackets.cpp +++ b/dNet/ClientPackets.cpp @@ -136,6 +136,33 @@ void ClientPackets::HandleClientPositionUpdate(const SystemAddress& sysAddr, Pac inStream.Read(angVelocity.z); } + // TODO figure out how to use these. Ignoring for now, but reading in if they exist. + bool hasLocalSpaceInfo{}; + LWOOBJID objectId{}; + NiPoint3 localSpacePosition{}; + bool hasLinearVelocity{}; + NiPoint3 linearVelocity{}; + if (inStream.Read(hasLocalSpaceInfo) && hasLocalSpaceInfo) { + inStream.Read(objectId); + inStream.Read(localSpacePosition.x); + inStream.Read(localSpacePosition.y); + inStream.Read(localSpacePosition.z); + if (inStream.Read(hasLinearVelocity) && hasLinearVelocity) { + inStream.Read(linearVelocity.x); + inStream.Read(linearVelocity.y); + inStream.Read(linearVelocity.z); + } + } + bool hasRemoteInputInfo{}; + RemoteInputInfo remoteInput{}; + + if (inStream.Read(hasRemoteInputInfo) && hasRemoteInputInfo) { + inStream.Read(remoteInput.m_RemoteInputX); + inStream.Read(remoteInput.m_RemoteInputY); + inStream.Read(remoteInput.m_IsPowersliding); + inStream.Read(remoteInput.m_IsModified); + } + bool updateChar = true; if (possessorComponent != nullptr) { @@ -150,9 +177,6 @@ void ClientPackets::HandleClientPositionUpdate(const SystemAddress& sysAddr, Pac auto* vehiclePhysicsComponent = possassableEntity->GetComponent(); if (vehiclePhysicsComponent != nullptr) { - // This is flipped for whatever reason - rotation = NiQuaternion(rotation.z, rotation.y, rotation.x, rotation.w); - vehiclePhysicsComponent->SetPosition(position); vehiclePhysicsComponent->SetRotation(rotation); vehiclePhysicsComponent->SetIsOnGround(onGround); @@ -161,6 +185,7 @@ void ClientPackets::HandleClientPositionUpdate(const SystemAddress& sysAddr, Pac vehiclePhysicsComponent->SetDirtyVelocity(velocityFlag); vehiclePhysicsComponent->SetAngularVelocity(angVelocity); vehiclePhysicsComponent->SetDirtyAngularVelocity(angVelocityFlag); + vehiclePhysicsComponent->SetRemoteInputInfo(remoteInput); } else { // Need to get the mount's controllable physics auto* controllablePhysicsComponent = possassableEntity->GetComponent(); diff --git a/dScripts/02_server/Map/GF/MastTeleport.cpp b/dScripts/02_server/Map/GF/MastTeleport.cpp index 311da34a..5f8795a3 100644 --- a/dScripts/02_server/Map/GF/MastTeleport.cpp +++ b/dScripts/02_server/Map/GF/MastTeleport.cpp @@ -43,15 +43,7 @@ void MastTeleport::OnTimerDone(Entity* self, std::string timerName) { GameMessages::SendTeleport(playerId, position, rotation, player->GetSystemAddress(), true); - // Hacky fix for odd rotations - auto mastName = self->GetVar(u"MastName"); - if (mastName == u"Elephant") { - GameMessages::SendOrientToAngle(playerId, true, (M_PI / 180) * 140.0f, player->GetSystemAddress()); - } else if (mastName == u"Jail") { - GameMessages::SendOrientToAngle(playerId, true, (M_PI / 180) * 100.0f, player->GetSystemAddress()); - } else if (mastName == u""){ - GameMessages::SendOrientToAngle(playerId, true, (M_PI / 180) * 203.0f, player->GetSystemAddress()); - } + GameMessages::SendTeleport(playerId, position, rotation, player->GetSystemAddress(), true); const auto cinematic = GeneralUtils::UTF16ToWTF8(self->GetVar(u"Cinematic")); const auto leanIn = self->GetVar(u"LeanIn"); diff --git a/dScripts/EquipmentScripts/BuccaneerValiantShip.cpp b/dScripts/EquipmentScripts/BuccaneerValiantShip.cpp index 3db214b5..15954622 100644 --- a/dScripts/EquipmentScripts/BuccaneerValiantShip.cpp +++ b/dScripts/EquipmentScripts/BuccaneerValiantShip.cpp @@ -11,7 +11,7 @@ void BuccaneerValiantShip::OnStartup(Entity* self) { // Kill self if missed self->AddCallbackTimer(1.1F, [self]() { - self->Kill(); + self->Smash(); }); } }); diff --git a/dZoneManager/Level.h b/dZoneManager/Level.h index 83daeedb..0f8fa72d 100644 --- a/dZoneManager/Level.h +++ b/dZoneManager/Level.h @@ -30,12 +30,6 @@ public: struct SceneObjectDataChunk { std::map objects; - SceneObject& GetObject(LWOOBJID id) { - for (std::map::iterator it = objects.begin(); it != objects.end(); ++it) { - if (it->first == id) return it->second; - } - } - const void PrintAllObjects() { for (std::map::iterator it = objects.begin(); it != objects.end(); ++it) { std::cout << "\t ID: " << it->first << " LOT: " << it->second.lot << std::endl;