diff --git a/dCommon/eUnequippableActiveType.h b/dCommon/eUnequippableActiveType.h new file mode 100644 index 00000000..8a6d29ba --- /dev/null +++ b/dCommon/eUnequippableActiveType.h @@ -0,0 +1,14 @@ +#pragma once + +#ifndef __EUNEQUIPPABLEACTIVETYPE__H__ +#define __EUNEQUIPPABLEACTIVETYPE__H__ + +#include + +enum class eUnequippableActiveType : int32_t { + INVALID = -1, + PET = 0, + MOUNT +}; + +#endif //!__EUNEQUIPPABLEACTIVETYPE__H__ diff --git a/dDatabase/Tables/CDItemComponentTable.cpp b/dDatabase/Tables/CDItemComponentTable.cpp index 703497ff..6ff192fd 100644 --- a/dDatabase/Tables/CDItemComponentTable.cpp +++ b/dDatabase/Tables/CDItemComponentTable.cpp @@ -45,7 +45,7 @@ CDItemComponentTable::CDItemComponentTable(void) { entry.offsetGroupID = tableData.getIntField(19, -1); entry.buildTypes = tableData.getIntField(20, -1); entry.reqPrecondition = tableData.getStringField(21, ""); - entry.animationFlag = tableData.getIntField(22, -1); + entry.animationFlag = tableData.getIntField(22, 0); entry.equipEffects = tableData.getIntField(23, -1); entry.readyForQA = tableData.getIntField(24, -1) == 1 ? true : false; entry.itemRating = tableData.getIntField(25, -1); @@ -123,7 +123,7 @@ const CDItemComponent& CDItemComponentTable::GetItemComponentByID(unsigned int s entry.offsetGroupID = tableData.getIntField(19, -1); entry.buildTypes = tableData.getIntField(20, -1); entry.reqPrecondition = tableData.getStringField(21, ""); - entry.animationFlag = tableData.getIntField(22, -1); + entry.animationFlag = tableData.getIntField(22, 0); entry.equipEffects = tableData.getIntField(23, -1); entry.readyForQA = tableData.getIntField(24, -1) == 1 ? true : false; entry.itemRating = tableData.getIntField(25, -1); diff --git a/dGame/Entity.cpp b/dGame/Entity.cpp index 89289004..52eddd06 100644 --- a/dGame/Entity.cpp +++ b/dGame/Entity.cpp @@ -1454,6 +1454,13 @@ void Entity::Smash(const LWOOBJID source, const eKillType killType, const std::u Kill(EntityManager::Instance()->GetEntity(source)); return; } + auto* possessorComponent = GetComponent(); + if (possessorComponent) { + if (possessorComponent->GetPossessable() != LWOOBJID_EMPTY) { + auto* mount = EntityManager::Instance()->GetEntity(possessorComponent->GetPossessable()); + if (mount) possessorComponent->Dismount(mount, true); + } + } destroyableComponent->Smash(source, killType, deathType); } diff --git a/dGame/dComponents/CharacterComponent.h b/dGame/dComponents/CharacterComponent.h index 8efb4ef5..196dfa01 100644 --- a/dGame/dComponents/CharacterComponent.h +++ b/dGame/dComponents/CharacterComponent.h @@ -167,18 +167,6 @@ public: */ void SetLastRocketItemID(LWOOBJID lastRocketItemID) { m_LastRocketItemID = lastRocketItemID; } - /** - * Gets the object ID of the mount item that is being used - * @return the object ID of the mount item that is being used - */ - LWOOBJID GetMountItemID() const { return m_MountItemID; } - - /** - * Sets the object ID of the mount item that is being used - * @param m_MountItemID the object ID of the mount item that is being used - */ - void SetMountItemID(LWOOBJID mountItemID) { m_MountItemID = mountItemID; } - /** * Gets the name of this character * @return the name of this character @@ -569,11 +557,6 @@ private: * ID of the last rocket used */ LWOOBJID m_LastRocketItemID = LWOOBJID_EMPTY; - - /** - * Mount Item ID - */ - LWOOBJID m_MountItemID = LWOOBJID_EMPTY; }; #endif // CHARACTERCOMPONENT_H diff --git a/dGame/dComponents/ControllablePhysicsComponent.cpp b/dGame/dComponents/ControllablePhysicsComponent.cpp index 471b9ca1..d7caf6a9 100644 --- a/dGame/dComponents/ControllablePhysicsComponent.cpp +++ b/dGame/dComponents/ControllablePhysicsComponent.cpp @@ -32,6 +32,7 @@ ControllablePhysicsComponent::ControllablePhysicsComponent(Entity* entity) : Com m_IgnoreMultipliers = false; m_PickupRadius = 0.0f; m_DirtyPickupRadiusScale = true; + m_IsTeleporting = false; if (entity->GetLOT() != 1) // Other physics entities we care about will be added by BaseCombatAI return; @@ -128,7 +129,10 @@ void ControllablePhysicsComponent::Serialize(RakNet::BitStream* outBitStream, bo outBitStream->Write0(); } - if (!bIsInitialUpdate) outBitStream->Write0(); + if (!bIsInitialUpdate) { + outBitStream->Write(m_IsTeleporting); + m_IsTeleporting = false; + } } void ControllablePhysicsComponent::LoadFromXml(tinyxml2::XMLDocument* doc) { diff --git a/dGame/dComponents/ControllablePhysicsComponent.h b/dGame/dComponents/ControllablePhysicsComponent.h index bc05657c..ac481b9f 100644 --- a/dGame/dComponents/ControllablePhysicsComponent.h +++ b/dGame/dComponents/ControllablePhysicsComponent.h @@ -220,6 +220,18 @@ public: */ bool GetStatic() const { return m_Static; } + /** + * Sets if the entity is Teleporting, + * @param value whether or not the entity is Is Teleporting + */ + void SetIsTeleporting(const bool value) { m_IsTeleporting = value; } + + /** + * Returns whether or not this entity is currently is teleporting + * @return whether or not this entity is currently is teleporting + */ + bool GetIsTeleporting() const { return m_IsTeleporting; } + /** * Returns the Physics entity for the component * @return Physics entity for the component @@ -355,6 +367,11 @@ private: * The active pickup radius for this entity */ float m_PickupRadius; + + /** + * If the entity is teleporting + */ + bool m_IsTeleporting; }; #endif // CONTROLLABLEPHYSICSCOMPONENT_H diff --git a/dGame/dComponents/DestroyableComponent.cpp b/dGame/dComponents/DestroyableComponent.cpp index 1cbf7aad..8b9ae42d 100644 --- a/dGame/dComponents/DestroyableComponent.cpp +++ b/dGame/dComponents/DestroyableComponent.cpp @@ -26,6 +26,8 @@ #include "MissionComponent.h" #include "CharacterComponent.h" +#include "PossessableComponent.h" +#include "PossessorComponent.h" #include "dZoneManager.h" DestroyableComponent::DestroyableComponent(Entity* parent) : Component(parent) { @@ -608,6 +610,24 @@ void DestroyableComponent::Damage(uint32_t damage, const LWOOBJID source, uint32 SetHealth(health); SetIsShielded(absorb > 0); + // Dismount on the possessable hit + auto possessable = m_Parent->GetComponent(); + if (possessable && possessable->GetDepossessOnHit()) { + possessable->Dismount(); + } + + // Dismount on the possessor hit + auto possessor = m_Parent->GetComponent(); + if (possessor) { + auto possessableId = possessor->GetPossessable(); + if (possessableId != LWOOBJID_EMPTY) { + auto possessable = EntityManager::Instance()->GetEntity(possessableId); + if (possessable) { + possessor->Dismount(possessable); + } + } + } + if (m_Parent->GetLOT() != 1) { echo = true; } diff --git a/dGame/dComponents/InventoryComponent.cpp b/dGame/dComponents/InventoryComponent.cpp index 6dbb3bd8..930ace8d 100644 --- a/dGame/dComponents/InventoryComponent.cpp +++ b/dGame/dComponents/InventoryComponent.cpp @@ -26,6 +26,7 @@ #include "DestroyableComponent.h" #include "dConfig.h" #include "eItemType.h" +#include "eUnequippableActiveType.h" InventoryComponent::InventoryComponent(Entity* parent, tinyxml2::XMLDocument* document) : Component(parent) { this->m_Dirty = true; @@ -62,23 +63,23 @@ InventoryComponent::InventoryComponent(Entity* parent, tinyxml2::XMLDocument* do const auto& info = Inventory::FindItemComponent(item.itemid); UpdateSlot(info.equipLocation, { id, static_cast(item.itemid), item.count, slot++ }); - + // Equip this items proxies. auto subItems = info.subItems; - + subItems.erase(std::remove_if(subItems.begin(), subItems.end(), ::isspace), subItems.end()); - + if (!subItems.empty()) { const auto subItemsSplit = GeneralUtils::SplitString(subItems, ','); - + for (auto proxyLotAsString : subItemsSplit) { const auto proxyLOT = static_cast(std::stoi(proxyLotAsString)); - + const auto& proxyInfo = Inventory::FindItemComponent(proxyLOT); const LWOOBJID proxyId = ObjectIDManager::Instance()->GenerateObjectID(); - + // Use item.count since we equip item.count number of the item this is a requested proxy of - UpdateSlot(proxyInfo.equipLocation, { proxyId, proxyLOT, item.count, slot++ } ); + UpdateSlot(proxyInfo.equipLocation, { proxyId, proxyLOT, item.count, slot++ }); } } } @@ -795,9 +796,7 @@ void InventoryComponent::RemoveSlot(const std::string& location) { } void InventoryComponent::EquipItem(Item* item, const bool skipChecks) { - if (!Inventory::IsValidItem(item->GetLot())) { - return; - } + if (!Inventory::IsValidItem(item->GetLot())) return; // Temp items should be equippable but other transfer items shouldn't be (for example the instruments in RB) if (item->IsEquipped() @@ -820,9 +819,7 @@ void InventoryComponent::EquipItem(Item* item, const bool skipChecks) { auto* characterComponent = m_Parent->GetComponent(); - if (characterComponent != nullptr) { - characterComponent->SetLastRocketItemID(item->GetId()); - } + if (characterComponent != nullptr) characterComponent->SetLastRocketItemID(item->GetId()); lauchPad->OnUse(m_Parent); @@ -836,94 +833,8 @@ void InventoryComponent::EquipItem(Item* item, const bool skipChecks) { const auto type = static_cast(item->GetInfo().itemType); - if (item->GetLot() == 8092 && m_Parent->GetGMLevel() >= GAME_MASTER_LEVEL_OPERATOR && hasCarEquipped == false) { - auto startPosition = m_Parent->GetPosition(); - auto startRotation = NiQuaternion::LookAt(startPosition, startPosition + NiPoint3::UNIT_X); - auto angles = startRotation.GetEulerAngles(); - angles.y -= PI; - startRotation = NiQuaternion::FromEulerAngles(angles); - - GameMessages::SendTeleport(m_Parent->GetObjectID(), startPosition, startRotation, m_Parent->GetSystemAddress(), true, true); - - EntityInfo info{}; - info.lot = 8092; - info.pos = startPosition; - info.rot = startRotation; - info.spawnerID = m_Parent->GetObjectID(); - - auto* carEntity = EntityManager::Instance()->CreateEntity(info, nullptr, m_Parent); - m_Parent->AddChild(carEntity); - - auto* destroyableComponent = carEntity->GetComponent(); - - // Setup the vehicle stats. - if (destroyableComponent != nullptr) { - destroyableComponent->SetIsSmashable(false); - destroyableComponent->SetIsImmune(true); - } - // #108 - auto* possessableComponent = carEntity->GetComponent(); - - if (possessableComponent != nullptr) { - previousPossessableID = possessableComponent->GetPossessor(); - possessableComponent->SetPossessor(m_Parent->GetObjectID()); - } - - auto* moduleAssemblyComponent = carEntity->GetComponent(); - - if (moduleAssemblyComponent != nullptr) { - moduleAssemblyComponent->SetSubKey(item->GetSubKey()); - moduleAssemblyComponent->SetUseOptionalParts(false); - - for (auto* config : item->GetConfig()) { - if (config->GetKey() == u"assemblyPartLOTs") { - moduleAssemblyComponent->SetAssemblyPartsLOTs(GeneralUtils::ASCIIToUTF16(config->GetValueAsString())); - } - } - } - // #107 - auto* possessorComponent = m_Parent->GetComponent(); - - if (possessorComponent) possessorComponent->SetPossessable(carEntity->GetObjectID()); - - auto* characterComponent = m_Parent->GetComponent(); - - if (characterComponent) characterComponent->SetIsRacing(true); - - EntityManager::Instance()->ConstructEntity(carEntity); - EntityManager::Instance()->SerializeEntity(m_Parent); - GameMessages::SendSetJetPackMode(m_Parent, false); - - GameMessages::SendNotifyVehicleOfRacingObject(carEntity->GetObjectID(), m_Parent->GetObjectID(), UNASSIGNED_SYSTEM_ADDRESS); - GameMessages::SendRacingPlayerLoaded(LWOOBJID_EMPTY, m_Parent->GetObjectID(), carEntity->GetObjectID(), UNASSIGNED_SYSTEM_ADDRESS); - GameMessages::SendVehicleUnlockInput(carEntity->GetObjectID(), false, UNASSIGNED_SYSTEM_ADDRESS); - GameMessages::SendTeleport(m_Parent->GetObjectID(), startPosition, startRotation, m_Parent->GetSystemAddress(), true, true); - GameMessages::SendTeleport(carEntity->GetObjectID(), startPosition, startRotation, m_Parent->GetSystemAddress(), true, true); - EntityManager::Instance()->SerializeEntity(m_Parent); - - hasCarEquipped = true; - equippedCarEntity = carEntity; - return; - } else if (item->GetLot() == 8092 && m_Parent->GetGMLevel() >= GAME_MASTER_LEVEL_OPERATOR && hasCarEquipped == true) { - GameMessages::SendNotifyRacingClient(LWOOBJID_EMPTY, 3, 0, LWOOBJID_EMPTY, u"", m_Parent->GetObjectID(), UNASSIGNED_SYSTEM_ADDRESS); - auto player = dynamic_cast(m_Parent); - player->SendToZone(player->GetCharacter()->GetZoneID()); - equippedCarEntity->Kill(); - hasCarEquipped = false; - equippedCarEntity = nullptr; - return; - } - - if (!building) { - if (item->GetLot() == 6086) { - return; - } - - if (type == eItemType::ITEM_TYPE_LOOT_MODEL || type == eItemType::ITEM_TYPE_VEHICLE) { - return; - } - } + if (!building && (item->GetLot() == 6086 || type == eItemType::ITEM_TYPE_LOOT_MODEL || type == eItemType::ITEM_TYPE_VEHICLE)) return; if (type != eItemType::ITEM_TYPE_LOOT_MODEL && type != eItemType::ITEM_TYPE_MODEL) { if (!item->GetBound() && !item->GetPreconditionExpression()->Check(m_Parent)) { @@ -940,9 +851,7 @@ void InventoryComponent::EquipItem(Item* item, const bool skipChecks) { set->OnEquip(lot); } - if (item->GetInfo().isBOE) { - item->SetBound(true); - } + if (item->GetInfo().isBOE) item->SetBound(true); GenerateProxies(item); @@ -989,6 +898,85 @@ void InventoryComponent::UnEquipItem(Item* item) { } } +void InventoryComponent::HandlePossession(Item* item) { + auto* characterComponent = m_Parent->GetComponent(); + if (!characterComponent) return; + + auto* possessorComponent = m_Parent->GetComponent(); + if (!possessorComponent) return; + + // Don't do anything if we are busy dismounting + if (possessorComponent->GetIsDismounting()) return; + + // Check to see if we are already mounting something + auto* currentlyPossessedEntity = EntityManager::Instance()->GetEntity(possessorComponent->GetPossessable()); + auto currentlyPossessedItem = possessorComponent->GetMountItemID(); + + if (currentlyPossessedItem) { + if (currentlyPossessedEntity) possessorComponent->Dismount(currentlyPossessedEntity); + return; + } + + GameMessages::SendSetStunned(m_Parent->GetObjectID(), eStunState::PUSH, m_Parent->GetSystemAddress(), LWOOBJID_EMPTY, true, false, true, false, false, false, false, true, true, true, true, true, true, true, true, true); + + // Set the mount Item ID so that we know what were handling + possessorComponent->SetMountItemID(item->GetId()); + GameMessages::SendSetMountInventoryID(m_Parent, item->GetId(), UNASSIGNED_SYSTEM_ADDRESS); + + // Create entity to mount + auto startRotation = m_Parent->GetRotation(); + + EntityInfo info{}; + info.lot = item->GetLot(); + info.pos = m_Parent->GetPosition(); + info.rot = startRotation; + info.spawnerID = m_Parent->GetObjectID(); + + auto* mount = EntityManager::Instance()->CreateEntity(info, nullptr, m_Parent); + + // Check to see if the mount is a vehicle, if so, flip it + auto* vehicleComponent = mount->GetComponent(); + if (vehicleComponent) { + auto angles = startRotation.GetEulerAngles(); + // Make it right side up + angles.x -= PI; + // Make it going in the direction of the player + angles.y -= PI; + startRotation = NiQuaternion::FromEulerAngles(angles); + mount->SetRotation(startRotation); + // We're pod racing now + characterComponent->SetIsRacing(true); + } + + // Setup the destroyable stats + auto* destroyableComponent = mount->GetComponent(); + if (destroyableComponent) { + destroyableComponent->SetIsSmashable(false); + destroyableComponent->SetIsImmune(true); + } + + // Mount it + auto* possessableComponent = mount->GetComponent(); + if (possessableComponent) { + possessableComponent->SetIsItemSpawned(true); + possessableComponent->SetPossessor(m_Parent->GetObjectID()); + // Possess it + possessorComponent->SetPossessable(mount->GetObjectID()); + possessorComponent->SetPossessableType(possessableComponent->GetPossessionType()); + } + + GameMessages::SendSetJetPackMode(m_Parent, false); + + // Make it go to the client + EntityManager::Instance()->ConstructEntity(mount); + // Update the possessor + EntityManager::Instance()->SerializeEntity(m_Parent); + + // have to unlock the input so it vehicle can be driven + if (vehicleComponent) GameMessages::SendVehicleUnlockInput(mount->GetObjectID(), false, m_Parent->GetSystemAddress()); + GameMessages::SendMarkInventoryItemAsActive(m_Parent->GetObjectID(), true, eUnequippableActiveType::MOUNT, item->GetId(), m_Parent->GetSystemAddress()); +} + void InventoryComponent::ApplyBuff(Item* item) const { const auto buffs = FindBuffs(item, true); diff --git a/dGame/dComponents/InventoryComponent.h b/dGame/dComponents/InventoryComponent.h index 01b4ca86..394cb801 100644 --- a/dGame/dComponents/InventoryComponent.h +++ b/dGame/dComponents/InventoryComponent.h @@ -198,6 +198,13 @@ public: */ void UnEquipItem(Item* item); + /** + * Unequips an Item from the inventory + * @param item the Item to unequip + * @return if we were successful + */ + void HandlePossession(Item* item); + /** * Adds a buff related to equipping a lot to the entity * @param item the item to find buffs for diff --git a/dGame/dComponents/PetComponent.cpp b/dGame/dComponents/PetComponent.cpp index ef882555..8863f9be 100644 --- a/dGame/dComponents/PetComponent.cpp +++ b/dGame/dComponents/PetComponent.cpp @@ -14,6 +14,7 @@ #include "dpWorld.h" #include "PetDigServer.h" #include "../dWorldServer/ObjectIDManager.h" +#include "eUnequippableActiveType.h" #include "Game.h" #include "dConfig.h" @@ -883,7 +884,7 @@ void PetComponent::Activate(Item* item, bool registerPet, bool fromTaming) { GameMessages::SendSetPetNameModerated(m_Owner, m_DatabaseId, m_ModerationStatus, owner->GetSystemAddress()); } - GameMessages::SendMarkInventoryItemAsActive(m_Owner, true, 0, m_ItemId, GetOwner()->GetSystemAddress()); + GameMessages::SendMarkInventoryItemAsActive(m_Owner, true, eUnequippableActiveType::PET, m_ItemId, GetOwner()->GetSystemAddress()); activePets[m_Owner] = m_Parent->GetObjectID(); @@ -945,7 +946,7 @@ void PetComponent::AddDrainImaginationTimer(Item* item, bool fromTaming) { void PetComponent::Deactivate() { GameMessages::SendPlayFXEffect(m_Parent->GetObjectID(), -1, u"despawn", "", LWOOBJID_EMPTY, 1, 1, true); - GameMessages::SendMarkInventoryItemAsActive(m_Owner, false, 0, m_ItemId, GetOwner()->GetSystemAddress()); + GameMessages::SendMarkInventoryItemAsActive(m_Owner, false, eUnequippableActiveType::PET, m_ItemId, GetOwner()->GetSystemAddress()); activePets.erase(m_Owner); diff --git a/dGame/dComponents/PossessableComponent.cpp b/dGame/dComponents/PossessableComponent.cpp index be31914b..37591532 100644 --- a/dGame/dComponents/PossessableComponent.cpp +++ b/dGame/dComponents/PossessableComponent.cpp @@ -18,7 +18,7 @@ PossessableComponent::PossessableComponent(Entity* parent, uint32_t componentId) // Should a result not exist for this default to attached visible if (!result.eof()) { - m_PossessionType = static_cast(result.getIntField(0, 0)); + m_PossessionType = static_cast(result.getIntField(0, 1)); // Default to Attached Visible m_DepossessOnHit = static_cast(result.getIntField(1, 0)); } else { m_PossessionType = ePossessionType::ATTACHED_VISIBLE; @@ -30,7 +30,7 @@ PossessableComponent::PossessableComponent(Entity* parent, uint32_t componentId) void PossessableComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags) { outBitStream->Write(m_DirtyPossessable || bIsInitialUpdate); if (m_DirtyPossessable || bIsInitialUpdate) { - m_DirtyPossessable = false; + m_DirtyPossessable = false; // reset flag outBitStream->Write(m_Possessor != LWOOBJID_EMPTY); if (m_Possessor != LWOOBJID_EMPTY) outBitStream->Write(m_Possessor); @@ -38,9 +38,18 @@ void PossessableComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsIn if (m_AnimationFlag != eAnimationFlags::IDLE_INVALID) outBitStream->Write(m_AnimationFlag); outBitStream->Write(m_ImmediatelyDepossess); + m_ImmediatelyDepossess = false; // reset flag } } -void PossessableComponent::OnUse(Entity* originator) { - // TODO: Implement this +void PossessableComponent::Dismount() { + SetPossessor(LWOOBJID_EMPTY); + if (m_ItemSpawned) m_Parent->ScheduleKillAfterUpdate(); +} + +void PossessableComponent::OnUse(Entity* originator) { + auto* possessor = originator->GetComponent(); + if (possessor) { + possessor->Mount(m_Parent); + } } diff --git a/dGame/dComponents/PossessableComponent.h b/dGame/dComponents/PossessableComponent.h index 77905c9c..43ce8610 100644 --- a/dGame/dComponents/PossessableComponent.h +++ b/dGame/dComponents/PossessableComponent.h @@ -20,14 +20,24 @@ public: void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags); /** - * Sets the possessor of this entity + * @brief mounts the Entity + */ + void Mount(); + + /** + * @brief dismounts the Entity + */ + void Dismount(); + + /** + * Sets the possessor of this Entity * @param value the ID of the possessor to set */ void SetPossessor(LWOOBJID value) { m_Possessor = value; m_DirtyPossessable = true; }; /** - * Returns the possessor of this entity - * @return the possessor of this entity + * Returns the possessor of this Entity + * @return the possessor of this Entity */ LWOOBJID GetPossessor() const { return m_Possessor; }; @@ -38,19 +48,19 @@ public: void SetAnimationFlag(eAnimationFlags value) { m_AnimationFlag = value; m_DirtyPossessable = true; }; /** - * Returns the possession type of this entity - * @return the possession type of this entity + * Returns the possession type of this Entity + * @return the possession type of this Entity */ ePossessionType GetPossessionType() const { return m_PossessionType; }; /** - * Returns if the entity should deposses on hit - * @return if the entity should deposses on hit + * Returns if the Entity should deposses on hit + * @return if the Entity should deposses on hit */ bool GetDepossessOnHit() const { return m_DepossessOnHit; }; /** - * Forcibly depossess the entity + * Forcibly depossess the Entity */ void ForceDepossess() { m_ImmediatelyDepossess = true; m_DirtyPossessable = true; }; @@ -58,13 +68,13 @@ public: * Set if the parent entity was spawned from an item * @param value if the parent entity was spawned from an item */ - void SetItemSpawned(bool value) { m_ItemSpawned = value; }; + void SetIsItemSpawned(bool value) { m_ItemSpawned = value; }; /** * Returns if the parent entity was spawned from an item * @return if the parent entity was spawned from an item */ - LWOOBJID GetItemSpawned() const { return m_ItemSpawned; }; + LWOOBJID GetIsItemSpawned() const { return m_ItemSpawned; }; /** * Handles an OnUsed event by some other entity, if said entity has a Possessor it becomes the possessor diff --git a/dGame/dComponents/PossessorComponent.cpp b/dGame/dComponents/PossessorComponent.cpp index 4ace324b..f0cad5ca 100644 --- a/dGame/dComponents/PossessorComponent.cpp +++ b/dGame/dComponents/PossessorComponent.cpp @@ -1,12 +1,28 @@ #include "PossessorComponent.h" +#include "PossessableComponent.h" +#include "CharacterComponent.h" +#include "EntityManager.h" +#include "GameMessages.h" +#include "eUnequippableActiveType.h" PossessorComponent::PossessorComponent(Entity* parent) : Component(parent) { m_Possessable = LWOOBJID_EMPTY; } -PossessorComponent::~PossessorComponent() {} - - +PossessorComponent::~PossessorComponent() { + if (m_Possessable != LWOOBJID_EMPTY) { + auto* mount = EntityManager::Instance()->GetEntity(m_Possessable); + if (mount) { + auto* possessable = mount->GetComponent(); + if (possessable) { + if (possessable->GetIsItemSpawned()) { + GameMessages::SendMarkInventoryItemAsActive(m_Parent->GetObjectID(), false, eUnequippableActiveType::MOUNT, GetMountItemID(), m_Parent->GetSystemAddress()); + } + possessable->Dismount(); + } + } + } +} void PossessorComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags) { outBitStream->Write(m_DirtyPossesor || bIsInitialUpdate); @@ -19,3 +35,48 @@ void PossessorComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInit outBitStream->Write(m_PossessableType); } } + +void PossessorComponent::Mount(Entity* mount) { + // Don't do anything if we are busy dismounting + if (GetIsDismounting() || !mount) return; + + GameMessages::SendSetMountInventoryID(m_Parent, mount->GetObjectID(), UNASSIGNED_SYSTEM_ADDRESS); + auto* possessableComponent = mount->GetComponent(); + if (possessableComponent) { + possessableComponent->SetPossessor(m_Parent->GetObjectID()); + SetPossessable(mount->GetObjectID()); + SetPossessableType(possessableComponent->GetPossessionType()); + } + + auto characterComponent = m_Parent->GetComponent(); + if (characterComponent) characterComponent->SetIsRacing(true); + + // GM's to send + GameMessages::SendSetJetPackMode(m_Parent, false); + GameMessages::SendVehicleUnlockInput(mount->GetObjectID(), false, m_Parent->GetSystemAddress()); + GameMessages::SendSetStunned(m_Parent->GetObjectID(), eStunState::PUSH, m_Parent->GetSystemAddress(), LWOOBJID_EMPTY, true, false, true, false, false, false, false, true, true, true, true, true, true, true, true, true); + + EntityManager::Instance()->SerializeEntity(m_Parent); + EntityManager::Instance()->SerializeEntity(mount); +} + +void PossessorComponent::Dismount(Entity* mount, bool forceDismount) { + // Don't do anything if we are busy dismounting + if (GetIsDismounting() || !mount) return; + SetIsDismounting(true); + + if (mount) { + auto* possessableComponent = mount->GetComponent(); + if (possessableComponent) { + possessableComponent->SetPossessor(LWOOBJID_EMPTY); + if (forceDismount) possessableComponent->ForceDepossess(); + } + EntityManager::Instance()->SerializeEntity(m_Parent); + EntityManager::Instance()->SerializeEntity(mount); + + auto characterComponent = m_Parent->GetComponent(); + if (characterComponent) characterComponent->SetIsRacing(false); + } + // Make sure we don't have wacky controls + GameMessages::SendSetPlayerControlScheme(m_Parent, eControlSceme::SCHEME_A); +} diff --git a/dGame/dComponents/PossessorComponent.h b/dGame/dComponents/PossessorComponent.h index 2735adac..00b24445 100644 --- a/dGame/dComponents/PossessorComponent.h +++ b/dGame/dComponents/PossessorComponent.h @@ -25,35 +25,63 @@ public: void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags); /** - * Sets the entity that this entity is possessing - * @param value the ID of the entity this ID should posess + * @brief Mounts the entity + * + * @param mount Entity to be mounted + */ + void Mount(Entity* mount); + + /** + * @brief Dismounts the entity + * + * @param mount Entity to be dismounted + * @param forceDismount Should we forcibly dismount the entity + */ + void Dismount(Entity* mount, bool forceDismount = false); + + /** + * Sets the ID that this entity is possessing + * @param value The ID that this entity is possessing */ void SetPossessable(LWOOBJID value) { m_Possessable = value; m_DirtyPossesor = true; } /** * Returns the entity that this entity is currently posessing - * @return the entity that this entity is currently posessing + * @return The entity that this entity is currently posessing */ LWOOBJID GetPossessable() const { return m_Possessable; } /** - * Sets if we are busy mounting or dismounting - * @param value if we are busy mounting or dismounting + * Sets if we are busy dismounting + * @param value If we are busy dismounting */ - void SetIsBusy(bool value) { m_IsBusy = value; } + void SetIsDismounting(bool value) { m_IsDismounting = value; } /** - * Returns if we are busy mounting or dismounting - * @return if we are busy mounting or dismounting + * Returns if we are busy dismounting + * @return If we are busy dismounting */ - bool GetIsBusy() const { return m_IsBusy; } + bool GetIsDismounting() const { return m_IsDismounting; } /** * Sets the possesible type that's currently used, merely used by the shooting gallery if it's 0 - * @param value the possesible type to set + * @param value The possesible type to set */ void SetPossessableType(ePossessionType value) { m_PossessableType = value; m_DirtyPossesor = true; } + + /** + * Gets the object ID of the mount item that is being used + * @return The object ID of the mount item that is being used + */ + LWOOBJID GetMountItemID() const { return m_MountItemID; } + + /** + * Sets the object ID of the mount item that is being used + * @param m_MountItemID The object ID of the mount item that is being used + */ + void SetMountItemID(LWOOBJID mountItemID) { m_MountItemID = mountItemID; } + private: /** @@ -68,14 +96,19 @@ private: ePossessionType m_PossessableType = ePossessionType::NO_POSSESSION; /** - * @brief if the possessor is dirty + * @brief If the possessor is dirty * */ bool m_DirtyPossesor = false; /** - * @brief if the possessor is busy mounting or dismounting + * @brief If the possessor is busy dismounting * */ - bool m_IsBusy = false; + bool m_IsDismounting = false; + + /** + * Mount Item ID + */ + LWOOBJID m_MountItemID = LWOOBJID_EMPTY; }; diff --git a/dGame/dGameMessages/GameMessageHandler.cpp b/dGame/dGameMessages/GameMessageHandler.cpp index b3b60fe7..9cf09bbc 100644 --- a/dGame/dGameMessages/GameMessageHandler.cpp +++ b/dGame/dGameMessages/GameMessageHandler.cpp @@ -653,6 +653,10 @@ void GameMessageHandler::HandleMessage(RakNet::BitStream* inStream, const System GameMessages::HandleUpdatePlayerStatistic(inStream, entity); break; + case GAME_MSG_DISMOUNT_COMPLETE: + GameMessages::HandleDismountComplete(inStream, entity, sysAddr); + break; + default: //Game::logger->Log("GameMessageHandler", "Unknown game message ID: %X", messageID); break; diff --git a/dGame/dGameMessages/GameMessages.cpp b/dGame/dGameMessages/GameMessages.cpp index 1645afc8..772cb691 100644 --- a/dGame/dGameMessages/GameMessages.cpp +++ b/dGame/dGameMessages/GameMessages.cpp @@ -27,6 +27,7 @@ #include "ChatPackets.h" #include "GameConfig.h" #include "RocketLaunchLupComponent.h" +#include "eUnequippableActiveType.h" #include #include @@ -3414,7 +3415,7 @@ void GameMessages::SendRegisterPetDBID(LWOOBJID objectId, LWOOBJID petDBID, cons SEND_PACKET; } -void GameMessages::SendMarkInventoryItemAsActive(LWOOBJID objectId, bool bActive, int32_t iType, LWOOBJID itemID, const SystemAddress& sysAddr) { +void GameMessages::SendMarkInventoryItemAsActive(LWOOBJID objectId, bool bActive, eUnequippableActiveType iType, LWOOBJID itemID, const SystemAddress& sysAddr) { CBITSTREAM; CMSGHEADER; @@ -3423,8 +3424,8 @@ void GameMessages::SendMarkInventoryItemAsActive(LWOOBJID objectId, bool bActive bitStream.Write(bActive); - bitStream.Write(iType != 0); - if (iType != 0) bitStream.Write(iType); + bitStream.Write(iType != eUnequippableActiveType::INVALID); + if (iType != eUnequippableActiveType::INVALID) bitStream.Write(iType); bitStream.Write(itemID != LWOOBJID_EMPTY); if (itemID != LWOOBJID_EMPTY) bitStream.Write(itemID); @@ -3861,7 +3862,6 @@ void GameMessages::SendDisplayChatBubble(LWOOBJID objectId, const std::u16string void GameMessages::SendSetMountInventoryID(Entity* entity, const LWOOBJID& objectID, const SystemAddress& sysAddr) { CBITSTREAM; CMSGHEADER; - bitStream.Write(entity->GetObjectID()); bitStream.Write(GAME_MSG::GAME_MSG_SET_MOUNT_INVENTORY_ID); bitStream.Write(objectID); @@ -3871,30 +3871,53 @@ void GameMessages::SendSetMountInventoryID(Entity* entity, const LWOOBJID& objec void GameMessages::HandleDismountComplete(RakNet::BitStream* inStream, Entity* entity, const SystemAddress& sysAddr) { + // Get the objectID from the bitstream LWOOBJID objectId{}; inStream->Read(objectId); - auto* mount = EntityManager::Instance()->GetEntity(objectId); + // If we aren't possessing somethings, the don't do anything if (objectId != LWOOBJID_EMPTY) { - PossessorComponent* possessor; - if (entity->TryGetComponent(COMPONENT_TYPE_POSSESSOR, possessor)) { - if (mount) { - possessor->SetIsBusy(false); - possessor->SetPossessable(LWOOBJID_EMPTY); - possessor->SetPossessableType(ePossessionType::NO_POSSESSION); + auto* possessorComponent = entity->GetComponent(); + auto* mount = EntityManager::Instance()->GetEntity(objectId); + // make sure we have the things we need and they aren't null + if (possessorComponent && mount) { + if (!possessorComponent->GetIsDismounting()) return; + possessorComponent->SetIsDismounting(false); + possessorComponent->SetPossessable(LWOOBJID_EMPTY); + possessorComponent->SetPossessableType(ePossessionType::NO_POSSESSION); - GameMessages::SendSetStunned(entity->GetObjectID(), eStunState::POP, UNASSIGNED_SYSTEM_ADDRESS, LWOOBJID_EMPTY, true, false, true, false, false, false, false, true, true, true, true, true, true, true, true, true); - - EntityManager::Instance()->SerializeEntity(entity); + // character related things + auto* character = entity->GetComponent(); + if (character) { + // If we had an active item turn it off + if (possessorComponent->GetMountItemID() != LWOOBJID_EMPTY) GameMessages::SendMarkInventoryItemAsActive(entity->GetObjectID(), false, eUnequippableActiveType::MOUNT, possessorComponent->GetMountItemID(), entity->GetSystemAddress()); + possessorComponent->SetMountItemID(LWOOBJID_EMPTY); } + + // Set that the controllabel phsyics comp is teleporting + auto* controllablePhysicsComponent = entity->GetComponent(); + if (controllablePhysicsComponent) controllablePhysicsComponent->SetIsTeleporting(true); + + // Call dismoint on the possessable comp to let it handle killing the possessable + auto* possessableComponent = mount->GetComponent(); + if (possessableComponent) possessableComponent->Dismount(); + + // Update the entity that was possessing + EntityManager::Instance()->SerializeEntity(entity); + + // We aren't mounted so remove the stun + GameMessages::SendSetStunned(entity->GetObjectID(), eStunState::POP, UNASSIGNED_SYSTEM_ADDRESS, LWOOBJID_EMPTY, true, false, true, false, false, false, false, true, true, true, true, true, true, true, true, true); } } } void GameMessages::HandleAcknowledgePossession(RakNet::BitStream* inStream, Entity* entity, const SystemAddress& sysAddr) { - Game::logger->Log("HandleAcknowledgePossession", "Got AcknowledgePossession from %i", entity->GetLOT()); EntityManager::Instance()->SerializeEntity(entity); + LWOOBJID objectId{}; + inStream->Read(objectId); + auto* mount = EntityManager::Instance()->GetEntity(objectId); + if (mount) EntityManager::Instance()->SerializeEntity(mount); } //Racing diff --git a/dGame/dGameMessages/GameMessages.h b/dGame/dGameMessages/GameMessages.h index 47e5f41c..31bdebb3 100644 --- a/dGame/dGameMessages/GameMessages.h +++ b/dGame/dGameMessages/GameMessages.h @@ -19,6 +19,7 @@ class NiQuaternion; class User; class Entity; class NiPoint3; +enum class eUnequippableActiveType; namespace GameMessages { class PropertyDataMessage; @@ -318,7 +319,7 @@ namespace GameMessages { void SendRegisterPetDBID(LWOOBJID objectId, LWOOBJID petDBID, const SystemAddress& sysAddr); - void SendMarkInventoryItemAsActive(LWOOBJID objectId, bool bActive, int32_t iType, LWOOBJID itemID, const SystemAddress& sysAddr); + void SendMarkInventoryItemAsActive(LWOOBJID objectId, bool bActive, eUnequippableActiveType iType, LWOOBJID itemID, const SystemAddress& sysAddr); void SendClientExitTamingMinigame(LWOOBJID objectId, bool bVoluntaryExit, const SystemAddress& sysAddr); diff --git a/dGame/dInventory/Item.cpp b/dGame/dInventory/Item.cpp index 62e198a3..695ab47b 100644 --- a/dGame/dInventory/Item.cpp +++ b/dGame/dInventory/Item.cpp @@ -10,6 +10,9 @@ #include "dLogger.h" #include "EntityManager.h" #include "RenderComponent.h" +#include "PossessableComponent.h" +#include "CharacterComponent.h" +#include "eItemType.h" class Inventory; @@ -71,6 +74,12 @@ Item::Item( id = GeneralUtils::SetBit(id, OBJECT_BIT_CHARACTER); id = GeneralUtils::SetBit(id, OBJECT_BIT_PERSISTENT); + const auto type = static_cast(info->itemType); + + if (type == eItemType::ITEM_TYPE_MOUNT) { + id = GeneralUtils::SetBit(id, OBJECT_BIT_CLIENT); + } + this->id = id; inventory->AddManagedItem(this); @@ -254,49 +263,34 @@ bool Item::Consume() { return success; } -bool Item::UseNonEquip() { - auto* compRegistryTable = CDClientManager::Instance()->GetTable("ComponentsRegistry"); - - const auto packageComponentId = compRegistryTable->GetByIDAndType(lot, COMPONENT_TYPE_PACKAGE); - - auto* packCompTable = CDClientManager::Instance()->GetTable("PackageComponent"); - - auto packages = packCompTable->Query([=](const CDPackageComponent entry) {return entry.id == static_cast(packageComponentId); }); - - const auto success = !packages.empty(); - - auto inventoryComponent = inventory->GetComponent(); - - auto playerEntity = inventoryComponent->GetParent(); - - if (subKey != LWOOBJID_EMPTY) { +void Item::UseNonEquip() { + const auto type = static_cast(info->itemType); + if (type == eItemType::ITEM_TYPE_MOUNT) { + GetInventory()->GetComponent()->HandlePossession(this); + } else if (type == eItemType::ITEM_TYPE_PET_INVENTORY_ITEM && subKey != LWOOBJID_EMPTY) { const auto& databasePet = GetInventory()->GetComponent()->GetDatabasePet(subKey); - if (databasePet.lot != LOT_NULL) { GetInventory()->GetComponent()->SpawnPet(this); - - return true; } - } - if (success && (playerEntity->GetGMLevel() >= eGameMasterLevel::GAME_MASTER_LEVEL_JUNIOR_DEVELOPER || this->GetPreconditionExpression()->Check(playerEntity))) { - auto* entityParent = inventory->GetComponent()->GetParent(); + } else if (type == eItemType::ITEM_TYPE_PACKAGE) { + auto* compRegistryTable = CDClientManager::Instance()->GetTable("ComponentsRegistry"); + const auto packageComponentId = compRegistryTable->GetByIDAndType(lot, COMPONENT_TYPE_PACKAGE); + auto* packCompTable = CDClientManager::Instance()->GetTable("PackageComponent"); + auto packages = packCompTable->Query([=](const CDPackageComponent entry) {return entry.id == static_cast(packageComponentId); }); - for (auto& pack : packages) { - std::unordered_map result{}; - - result = LootGenerator::Instance().RollLootMatrix(entityParent, pack.LootMatrixIndex); - - if (!inventory->GetComponent()->HasSpaceForLoot(result)) { - return false; + const auto success = !packages.empty(); + if (success) { + auto* entityParent = inventory->GetComponent()->GetParent(); + for (auto& pack : packages) { + std::unordered_map result{}; + result = LootGenerator::Instance().RollLootMatrix(entityParent, pack.LootMatrixIndex); + if (!inventory->GetComponent()->HasSpaceForLoot(result)) { + } + LootGenerator::Instance().GiveLoot(inventory->GetComponent()->GetParent(), result, eLootSourceType::LOOT_SOURCE_CONSUMPTION); } - - LootGenerator::Instance().GiveLoot(inventory->GetComponent()->GetParent(), result, eLootSourceType::LOOT_SOURCE_CONSUMPTION); + inventory->GetComponent()->RemoveItem(lot, 1); } - Game::logger->Log("Item", "Used (%i)", lot); - inventory->GetComponent()->RemoveItem(lot, 1); } - - return success; } void Item::Disassemble(const eInventoryType inventoryType) { diff --git a/dGame/dInventory/Item.h b/dGame/dInventory/Item.h index 0613ca85..db7e246a 100644 --- a/dGame/dInventory/Item.h +++ b/dGame/dInventory/Item.h @@ -193,9 +193,8 @@ public: /** * Uses this item if its non equip, essentially an interface for the linked GM - * @return whether the use was successful, e.g. the skill was cast */ - bool UseNonEquip(); + void UseNonEquip(); /** * Disassembles the part LOTs of this item back into the inventory, if it has any diff --git a/dGame/dUtilities/SlashCommandHandler.cpp b/dGame/dUtilities/SlashCommandHandler.cpp index b91021de..c6f1da9f 100644 --- a/dGame/dUtilities/SlashCommandHandler.cpp +++ b/dGame/dUtilities/SlashCommandHandler.cpp @@ -419,6 +419,11 @@ void SlashCommandHandler::HandleChatCommand(const std::u16string& command, Entit if ((chatCommand == "playanimation" || chatCommand == "playanim") && args.size() == 1 && entity->GetGMLevel() >= GAME_MASTER_LEVEL_DEVELOPER) { std::u16string anim = GeneralUtils::ASCIIToUTF16(args[0], args[0].size()); GameMessages::SendPlayAnimation(entity, anim); + auto* possessorComponent = entity->GetComponent(); + if (possessorComponent) { + auto* possessedComponent = EntityManager::Instance()->GetEntity(possessorComponent->GetPossessable()); + if (possessedComponent) GameMessages::SendPlayAnimation(possessedComponent, anim); + } } if (chatCommand == "list-spawns" && entity->GetGMLevel() >= GAME_MASTER_LEVEL_DEVELOPER) { @@ -471,12 +476,24 @@ void SlashCommandHandler::HandleChatCommand(const std::u16string& command, Entit auto* controllablePhysicsComponent = entity->GetComponent(); - if (controllablePhysicsComponent == nullptr) { - return; - } - + if (!controllablePhysicsComponent) return; controllablePhysicsComponent->SetSpeedMultiplier(boost); + // speedboost possesables + auto possessor = entity->GetComponent(); + if (possessor) { + auto possessedID = possessor->GetPossessable(); + if (possessedID != LWOOBJID_EMPTY) { + auto possessable = EntityManager::Instance()->GetEntity(possessedID); + if (possessable) { + auto* possessControllablePhysicsComponent = possessable->GetComponent(); + if (possessControllablePhysicsComponent) { + possessControllablePhysicsComponent->SetSpeedMultiplier(boost); + } + } + } + } + EntityManager::Instance()->SerializeEntity(entity); } @@ -894,21 +911,17 @@ void SlashCommandHandler::HandleChatCommand(const std::u16string& command, Entit ChatPackets::SendSystemMessage(sysAddr, u"Correct usage: /teleport () - if no Y given, will teleport to the height of the terrain (or any physics object)."); } - auto* possessorComponent = entity->GetComponent(); - if (possessorComponent != nullptr) { + auto* possessorComponent = entity->GetComponent(); + if (possessorComponent) { auto* possassableEntity = EntityManager::Instance()->GetEntity(possessorComponent->GetPossessable()); if (possassableEntity != nullptr) { auto* vehiclePhysicsComponent = possassableEntity->GetComponent(); - - if (vehiclePhysicsComponent != nullptr) { + if (vehiclePhysicsComponent) { vehiclePhysicsComponent->SetPosition(pos); - EntityManager::Instance()->SerializeEntity(possassableEntity); - - Game::logger->Log("ClientPackets", "Forced updated vehicle position"); - } + } else GameMessages::SendTeleport(possassableEntity->GetObjectID(), pos, NiQuaternion(), sysAddr); } } } @@ -926,18 +939,12 @@ void SlashCommandHandler::HandleChatCommand(const std::u16string& command, Entit } if (chatCommand == "dismount" && entity->GetGMLevel() >= GAME_MASTER_LEVEL_DEVELOPER) { - PossessorComponent* possessorComponent; - if (entity->TryGetComponent(COMPONENT_TYPE_POSSESSOR, possessorComponent)) { - Entity* vehicle = EntityManager::Instance()->GetEntity(possessorComponent->GetPossessable()); - if (!vehicle) return; - - PossessableComponent* possessableComponent; - if (vehicle->TryGetComponent(COMPONENT_TYPE_POSSESSABLE, possessableComponent)) { - possessableComponent->SetPossessor(LWOOBJID_EMPTY); - possessorComponent->SetPossessable(LWOOBJID_EMPTY); - - EntityManager::Instance()->SerializeEntity(vehicle); - EntityManager::Instance()->SerializeEntity(entity); + auto* possessorComponent = entity->GetComponent(); + if (possessorComponent) { + auto possessableId = possessorComponent->GetPossessable(); + if (possessableId != LWOOBJID_EMPTY) { + auto* possessableEntity = EntityManager::Instance()->GetEntity(possessableId); + if (possessableEntity) possessorComponent->Dismount(possessableEntity, true); } } } @@ -1231,6 +1238,18 @@ void SlashCommandHandler::HandleChatCommand(const std::u16string& command, Entit return; } + auto vehiclePhysicsComponent = newEntity->GetComponent(); + if (vehiclePhysicsComponent) { + auto newRot = newEntity->GetRotation(); + auto angles = newRot.GetEulerAngles(); + // make it right side up + angles.x -= PI; + // make it going in the direction of the player + angles.y -= PI; + newRot = NiQuaternion::FromEulerAngles(angles); + newEntity->SetRotation(newRot); + } + EntityManager::Instance()->ConstructEntity(newEntity); } @@ -1520,7 +1539,33 @@ void SlashCommandHandler::HandleChatCommand(const std::u16string& command, Entit return; } - GameMessages::SendVehicleAddPassiveBoostAction(vehicle->GetObjectID(), UNASSIGNED_SYSTEM_ADDRESS); + if (args.size() >= 1) { + float time; + + if (!GeneralUtils::TryParse(args[0], time)) { + ChatPackets::SendSystemMessage(sysAddr, u"Invalid boost time."); + return; + } else { + GameMessages::SendVehicleAddPassiveBoostAction(vehicle->GetObjectID(), UNASSIGNED_SYSTEM_ADDRESS); + entity->AddCallbackTimer(time, [vehicle]() { + if (!vehicle) return; + GameMessages::SendVehicleRemovePassiveBoostAction(vehicle->GetObjectID(), UNASSIGNED_SYSTEM_ADDRESS); + }); + } + } else { + GameMessages::SendVehicleAddPassiveBoostAction(vehicle->GetObjectID(), UNASSIGNED_SYSTEM_ADDRESS); + } + + } + + if ((chatCommand == "unboost") && entity->GetGMLevel() >= GAME_MASTER_LEVEL_DEVELOPER) { + auto* possessorComponent = entity->GetComponent(); + + if (possessorComponent == nullptr) return; + auto* vehicle = EntityManager::Instance()->GetEntity(possessorComponent->GetPossessable()); + + if (vehicle == nullptr) return; + GameMessages::SendVehicleRemovePassiveBoostAction(vehicle->GetObjectID(), UNASSIGNED_SYSTEM_ADDRESS); } if (chatCommand == "activatespawner" && entity->GetGMLevel() >= GAME_MASTER_LEVEL_DEVELOPER && args.size() >= 1) { diff --git a/dNet/ClientPackets.cpp b/dNet/ClientPackets.cpp index b49801cc..39e835d2 100644 --- a/dNet/ClientPackets.cpp +++ b/dNet/ClientPackets.cpp @@ -140,14 +140,19 @@ void ClientPackets::HandleClientPositionUpdate(const SystemAddress& sysAddr, Pac inStream.Read(angVelocity.z); } - bool hasVehicle = false; + bool updateChar = true; if (possessorComponent != nullptr) { auto* possassableEntity = EntityManager::Instance()->GetEntity(possessorComponent->GetPossessable()); if (possassableEntity != nullptr) { - auto* vehiclePhysicsComponent = possassableEntity->GetComponent(); + auto* possessableComponent = possassableEntity->GetComponent(); + if (possessableComponent) { + // While possessing something, only update char if we are attached to the thing we are possessing + if (possessableComponent->GetPossessionType() != ePossessionType::ATTACHED_VISIBLE) updateChar = false; + } + auto* vehiclePhysicsComponent = possassableEntity->GetComponent(); if (vehiclePhysicsComponent != nullptr) { // This is flipped for whatever reason rotation = NiQuaternion(rotation.z, rotation.y, rotation.x, rotation.w); @@ -160,19 +165,30 @@ void ClientPackets::HandleClientPositionUpdate(const SystemAddress& sysAddr, Pac vehiclePhysicsComponent->SetDirtyVelocity(velocityFlag); vehiclePhysicsComponent->SetAngularVelocity(angVelocity); vehiclePhysicsComponent->SetDirtyAngularVelocity(angVelocityFlag); - - EntityManager::Instance()->SerializeEntity(possassableEntity); - - hasVehicle = true; + } else { + // Need to get the mount's controllable physics + auto* controllablePhysicsComponent = possassableEntity->GetComponent(); + if (!controllablePhysicsComponent) return; + controllablePhysicsComponent->SetPosition(position); + controllablePhysicsComponent->SetRotation(rotation); + controllablePhysicsComponent->SetIsOnGround(onGround); + controllablePhysicsComponent->SetIsOnRail(onRail); + controllablePhysicsComponent->SetVelocity(velocity); + controllablePhysicsComponent->SetDirtyVelocity(velocityFlag); + controllablePhysicsComponent->SetAngularVelocity(angVelocity); + controllablePhysicsComponent->SetDirtyAngularVelocity(angVelocityFlag); } + EntityManager::Instance()->SerializeEntity(possassableEntity); } } - if (hasVehicle) { + if (!updateChar) { velocity = NiPoint3::ZERO; angVelocity = NiPoint3::ZERO; } + + // Handle statistics auto* characterComponent = entity->GetComponent(); if (characterComponent != nullptr) { @@ -192,9 +208,7 @@ void ClientPackets::HandleClientPositionUpdate(const SystemAddress& sysAddr, Pac player->SetGhostReferencePoint(position); EntityManager::Instance()->QueueGhostUpdate(player->GetObjectID()); - if (!hasVehicle) { - EntityManager::Instance()->SerializeEntity(entity); - } + if (updateChar) EntityManager::Instance()->SerializeEntity(entity); //TODO: add moving platform stuffs /*bool movingPlatformFlag; diff --git a/docs/Commands.md b/docs/Commands.md index 8cefaa14..95ec28f9 100644 --- a/docs/Commands.md +++ b/docs/Commands.md @@ -63,7 +63,8 @@ These commands are primarily for development and testing. The usage of many of t |teleport|`/teleport (y) `|Teleports you. If no Y is given, you are teleported to the height of the terrain or physics object at (x, z). Alias: `/tele`.|6| |activatespawner|`/activatespawner `|Activates spawner by name.|8| |addmission|`/addmission `|Accepts the mission, adding it to your journal.|8| -|boost|`/boost`|Adds a passive boost action if you are in a vehicle.|8| +|boost|`/boost (time)`|Adds a passive boost action if you are in a vehicle. If time is given it will end after that amount of time|8| +|unboost|`/unboost`|Removes a passive vehicle boost|8| |buff|`/buff `|Applies the buff with the given id for the given number of seconds.|8| |buffme|`/buffme`|Sets health, armor, and imagination to 999.|8| |buffmed|`/buffmed`|Sets health, armor, and imagination to 9.|8| @@ -71,7 +72,7 @@ These commands are primarily for development and testing. The usage of many of t |completemission|`/completemission `|Completes the mission, removing it from your journal.|8| |createprivate|`/createprivate `|Creates a private zone with password.|8| |debugui|`/debugui`|Toggle Debug UI.|8| -|dismount|`/dismount`|Dismounts you from the vehicle.|8| +|dismount|`/dismount`|Dismounts you from the vehicle or mount.|8| |force-save|`/force-save`|While saving to database usually happens on regular intervals and when you disconnect from the server, this command saves your player's data to the database.|8| |freecam|`/freecam`|Toggles freecam mode.|8| |freemoney|`/freemoney `|Gives coins.|8|