Mounts v2 (#726)

* Mounts -v2

* fix stun state and make comments a bit nicer

* remove extra serilization

* update the char position a bit more correctly

* make vehicles face thr player's direction

* address feedback

* fix compiling for real this time

* removed uneeded check
This commit is contained in:
Aaron Kimbrell 2022-09-02 13:49:19 -05:00 committed by GitHub
parent 1af3e59348
commit 26f2eb409f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 480 additions and 245 deletions

View File

@ -0,0 +1,14 @@
#pragma once
#ifndef __EUNEQUIPPABLEACTIVETYPE__H__
#define __EUNEQUIPPABLEACTIVETYPE__H__
#include <cstdint>
enum class eUnequippableActiveType : int32_t {
INVALID = -1,
PET = 0,
MOUNT
};
#endif //!__EUNEQUIPPABLEACTIVETYPE__H__

View File

@ -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);

View File

@ -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<PossessorComponent>();
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);
}

View File

@ -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

View File

@ -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) {

View File

@ -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

View File

@ -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<PossessableComponent>();
if (possessable && possessable->GetDepossessOnHit()) {
possessable->Dismount();
}
// Dismount on the possessor hit
auto possessor = m_Parent->GetComponent<PossessorComponent>();
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;
}

View File

@ -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;
@ -78,7 +79,7 @@ InventoryComponent::InventoryComponent(Entity* parent, tinyxml2::XMLDocument* do
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<CharacterComponent>();
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<eItemType>(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<DestroyableComponent>();
// Setup the vehicle stats.
if (destroyableComponent != nullptr) {
destroyableComponent->SetIsSmashable(false);
destroyableComponent->SetIsImmune(true);
}
// #108
auto* possessableComponent = carEntity->GetComponent<PossessableComponent>();
if (possessableComponent != nullptr) {
previousPossessableID = possessableComponent->GetPossessor();
possessableComponent->SetPossessor(m_Parent->GetObjectID());
}
auto* moduleAssemblyComponent = carEntity->GetComponent<ModuleAssemblyComponent>();
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<PossessorComponent>();
if (possessorComponent) possessorComponent->SetPossessable(carEntity->GetObjectID());
auto* characterComponent = m_Parent->GetComponent<CharacterComponent>();
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<Player*>(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<CharacterComponent>();
if (!characterComponent) return;
auto* possessorComponent = m_Parent->GetComponent<PossessorComponent>();
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<VehiclePhysicsComponent>();
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<DestroyableComponent>();
if (destroyableComponent) {
destroyableComponent->SetIsSmashable(false);
destroyableComponent->SetIsImmune(true);
}
// Mount it
auto* possessableComponent = mount->GetComponent<PossessableComponent>();
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);

View File

@ -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

View File

@ -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);

View File

@ -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<ePossessionType>(result.getIntField(0, 0));
m_PossessionType = static_cast<ePossessionType>(result.getIntField(0, 1)); // Default to Attached Visible
m_DepossessOnHit = static_cast<bool>(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<PossessorComponent>();
if (possessor) {
possessor->Mount(m_Parent);
}
}

View File

@ -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

View File

@ -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<PossessableComponent>();
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<PossessableComponent>();
if (possessableComponent) {
possessableComponent->SetPossessor(m_Parent->GetObjectID());
SetPossessable(mount->GetObjectID());
SetPossessableType(possessableComponent->GetPossessionType());
}
auto characterComponent = m_Parent->GetComponent<CharacterComponent>();
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<PossessableComponent>();
if (possessableComponent) {
possessableComponent->SetPossessor(LWOOBJID_EMPTY);
if (forceDismount) possessableComponent->ForceDepossess();
}
EntityManager::Instance()->SerializeEntity(m_Parent);
EntityManager::Instance()->SerializeEntity(mount);
auto characterComponent = m_Parent->GetComponent<CharacterComponent>();
if (characterComponent) characterComponent->SetIsRacing(false);
}
// Make sure we don't have wacky controls
GameMessages::SendSetPlayerControlScheme(m_Parent, eControlSceme::SCHEME_A);
}

View File

@ -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;
};

View File

@ -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;

View File

@ -27,6 +27,7 @@
#include "ChatPackets.h"
#include "GameConfig.h"
#include "RocketLaunchLupComponent.h"
#include "eUnequippableActiveType.h"
#include <sstream>
#include <future>
@ -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<PossessorComponent>();
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<CharacterComponent>();
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<ControllablePhysicsComponent>();
if (controllablePhysicsComponent) controllablePhysicsComponent->SetIsTeleporting(true);
// Call dismoint on the possessable comp to let it handle killing the possessable
auto* possessableComponent = mount->GetComponent<PossessableComponent>();
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

View File

@ -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);

View File

@ -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<eItemType>(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() {
void Item::UseNonEquip() {
const auto type = static_cast<eItemType>(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);
}
} else if (type == eItemType::ITEM_TYPE_PACKAGE) {
auto* compRegistryTable = CDClientManager::Instance()->GetTable<CDComponentsRegistryTable>("ComponentsRegistry");
const auto packageComponentId = compRegistryTable->GetByIDAndType(lot, COMPONENT_TYPE_PACKAGE);
auto* packCompTable = CDClientManager::Instance()->GetTable<CDPackageComponentTable>("PackageComponent");
auto packages = packCompTable->Query([=](const CDPackageComponent entry) {return entry.id == static_cast<uint32_t>(packageComponentId); });
const auto success = !packages.empty();
auto inventoryComponent = inventory->GetComponent();
auto playerEntity = inventoryComponent->GetParent();
if (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))) {
if (success) {
auto* entityParent = inventory->GetComponent()->GetParent();
for (auto& pack : packages) {
std::unordered_map<LOT, int32_t> result{};
result = LootGenerator::Instance().RollLootMatrix(entityParent, pack.LootMatrixIndex);
if (!inventory->GetComponent()->HasSpaceForLoot(result)) {
return false;
}
LootGenerator::Instance().GiveLoot(inventory->GetComponent()->GetParent(), result, eLootSourceType::LOOT_SOURCE_CONSUMPTION);
}
Game::logger->Log("Item", "Used (%i)", lot);
inventory->GetComponent()->RemoveItem(lot, 1);
}
return success;
}
}
void Item::Disassemble(const eInventoryType inventoryType) {

View File

@ -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

View File

@ -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<PossessorComponent>();
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<ControllablePhysicsComponent>();
if (controllablePhysicsComponent == nullptr) {
return;
}
if (!controllablePhysicsComponent) return;
controllablePhysicsComponent->SetSpeedMultiplier(boost);
// speedboost possesables
auto possessor = entity->GetComponent<PossessorComponent>();
if (possessor) {
auto possessedID = possessor->GetPossessable();
if (possessedID != LWOOBJID_EMPTY) {
auto possessable = EntityManager::Instance()->GetEntity(possessedID);
if (possessable) {
auto* possessControllablePhysicsComponent = possessable->GetComponent<ControllablePhysicsComponent>();
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 <x> (<y>) <z> - if no Y given, will teleport to the height of the terrain (or any physics object).");
}
auto* possessorComponent = entity->GetComponent<PossessorComponent>();
if (possessorComponent != nullptr) {
auto* possessorComponent = entity->GetComponent<PossessorComponent>();
if (possessorComponent) {
auto* possassableEntity = EntityManager::Instance()->GetEntity(possessorComponent->GetPossessable());
if (possassableEntity != nullptr) {
auto* vehiclePhysicsComponent = possassableEntity->GetComponent<VehiclePhysicsComponent>();
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<PossessorComponent>();
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<VehiclePhysicsComponent>();
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;
}
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<PossessorComponent>();
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) {

View File

@ -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<VehiclePhysicsComponent>();
auto* possessableComponent = possassableEntity->GetComponent<PossessableComponent>();
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<VehiclePhysicsComponent>();
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);
} else {
// Need to get the mount's controllable physics
auto* controllablePhysicsComponent = possassableEntity->GetComponent<ControllablePhysicsComponent>();
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);
hasVehicle = true;
}
}
}
if (hasVehicle) {
if (!updateChar) {
velocity = NiPoint3::ZERO;
angVelocity = NiPoint3::ZERO;
}
// Handle statistics
auto* characterComponent = entity->GetComponent<CharacterComponent>();
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;

View File

@ -63,7 +63,8 @@ These commands are primarily for development and testing. The usage of many of t
|teleport|`/teleport <x> (y) <z>`|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 <spawner name>`|Activates spawner by name.|8|
|addmission|`/addmission <mission id>`|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 <id> <duration>`|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 <mission id>`|Completes the mission, removing it from your journal.|8|
|createprivate|`/createprivate <zone id> <clone id> <password>`|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 <coins>`|Gives coins.|8|