Merge remote-tracking branch 'upstream/main' into bbbfix

This commit is contained in:
EmosewaMC 2022-09-03 16:41:26 -07:00
commit caffa94141
44 changed files with 894 additions and 277 deletions

View File

@ -27,11 +27,11 @@ Darkflame Universe is a server emulator and does not distribute any LEGO® Unive
Development of the latest iteration of Darkflame Universe has been done primarily in a Unix-like environment and is where it has been tested and designed for deployment. It is therefore highly recommended that Darkflame Universe be built and deployed using a Unix-like environment for the most streamlined experience. Development of the latest iteration of Darkflame Universe has been done primarily in a Unix-like environment and is where it has been tested and designed for deployment. It is therefore highly recommended that Darkflame Universe be built and deployed using a Unix-like environment for the most streamlined experience.
### Prerequisites ### Prerequisites
**Clone the repository** #### Clone the repository
```bash ```bash
git clone --recursive https://github.com/DarkflameUniverse/DarkflameServer git clone --recursive https://github.com/DarkflameUniverse/DarkflameServer
``` ```
**Python** #### Python
Some tools utilized to streamline the setup process require Python 3, make sure you have it installed. Some tools utilized to streamline the setup process require Python 3, make sure you have it installed.
@ -52,7 +52,7 @@ Make sure packages like `gcc`, `cmake`, and `zlib` are installed. Depending on t
CMake must be version 3.14 or higher! CMake must be version 3.14 or higher!
**Build the repository** #### Build the repository
You can either run `build.sh` when in the root folder of the repository: You can either run `build.sh` when in the root folder of the repository:
@ -70,8 +70,8 @@ cd build
# Run CMake to generate make files # Run CMake to generate make files
cmake .. cmake ..
# Run make to build the project. To build utilizing multiple cores, append `-j` and the amount of cores to utilize, for example `make -j8` # To build utilizing multiple cores, append `-j` and the amount of cores to utilize, for example `cmake --build . --config Release -j8'
make cmake --build . --config Release
``` ```
### MacOS builds ### MacOS builds
@ -93,7 +93,7 @@ cmake --build . --config Release
### Windows builds (native) ### Windows builds (native)
Ensure that you have either the [MSVC](https://visualstudio.microsoft.com/vs/) or the [Clang](https://github.com/llvm/llvm-project/releases/) (recommended) compiler installed. You will also need to install [CMake](https://cmake.org/download/). Currently on native Windows the server will only work in Release mode. Ensure that you have either the [MSVC](https://visualstudio.microsoft.com/vs/) or the [Clang](https://github.com/llvm/llvm-project/releases/) (recommended) compiler installed. You will also need to install [CMake](https://cmake.org/download/). Currently on native Windows the server will only work in Release mode.
**Build the repository** #### Build the repository
```batch ```batch
:: Create the build directory :: Create the build directory
mkdir build mkdir build
@ -105,7 +105,7 @@ cmake ..
:: Run CMake with build flag to build :: Run CMake with build flag to build
cmake --build . --config Release cmake --build . --config Release
``` ```
**Windows for ARM** has not been tested but should build by doing the following #### Windows for ARM has not been tested but should build by doing the following
```batch ```batch
:: Create the build directory :: Create the build directory
mkdir build mkdir build
@ -121,13 +121,13 @@ cmake --build . --config Release
### Windows builds (WSL) ### Windows builds (WSL)
This section will go through how to install [WSL](https://docs.microsoft.com/en-us/windows/wsl/install) and building in a Linux environment under Windows. WSL requires Windows 10 version 2004 and higher (Build 19041 and higher) or Windows 11. This section will go through how to install [WSL](https://docs.microsoft.com/en-us/windows/wsl/install) and building in a Linux environment under Windows. WSL requires Windows 10 version 2004 and higher (Build 19041 and higher) or Windows 11.
**Open the Command Prompt application with Administrator permissions and run the following:** #### Open the Command Prompt application with Administrator permissions and run the following:
```bash ```bash
# Installing Windows Subsystem for Linux # Installing Windows Subsystem for Linux
wsl --install wsl --install
``` ```
**Open the Ubuntu application and run the following:** #### Open the Ubuntu application and run the following:
```bash ```bash
# Make sure the install is up to date # Make sure the install is up to date
apt update && apt upgrade apt update && apt upgrade
@ -159,7 +159,7 @@ now follow the build section for your system
### Resources ### Resources
**LEGO® Universe 1.10.64** #### LEGO® Universe 1.10.64
This repository does not distribute any LEGO® Universe files. A full install of LEGO® Universe version 1.10.64 (latest) is required to finish setting up Darkflame Universe. This repository does not distribute any LEGO® Universe files. A full install of LEGO® Universe version 1.10.64 (latest) is required to finish setting up Darkflame Universe.
@ -182,20 +182,20 @@ shasum -a 256 <file>
certutil -hashfile <file> SHA256 certutil -hashfile <file> SHA256
``` ```
**Unpacking the client** #### Unpacking the client
* Clone lcdr's utilities repository [here](https://github.com/lcdr/utils) * Clone lcdr's utilities repository [here](https://github.com/lcdr/utils)
* Use `pkextractor.pyw` to unpack the client files if they are not already unpacked * Use `pkextractor.pyw` to unpack the client files if they are not already unpacked
**Setup resource directory** #### Setup resource directory
* In the `build` directory create a `res` directory if it does not already exist. * In the `build` directory create a `res` directory if it does not already exist.
* Copy over or create symlinks from `macros`, `BrickModels`, `chatplus_en_us.txt`, `names`, and `maps` in your client `res` directory to the server `build/res` directory * Copy over or create symlinks from `macros`, `BrickModels`, `chatplus_en_us.txt`, `names`, and `maps` in your client `res` directory to the server `build/res` directory
* Unzip the navmeshes [here](./resources/navmeshes.zip) and place them in `build/res/maps/navmeshes` * Unzip the navmeshes [here](./resources/navmeshes.zip) and place them in `build/res/maps/navmeshes`
**Setup locale** #### Setup locale
* In the `build` directory create a `locale` directory if it does not already exist * In the `build` directory create a `locale` directory if it does not already exist
* Copy over or create symlinks from `locale.xml` in your client `locale` directory to the `build/locale` directory * Copy over or create symlinks from `locale.xml` in your client `locale` directory to the `build/locale` directory
**Client database** #### Client database
* Use `fdb_to_sqlite.py` in lcdr's utilities on `res/cdclient.fdb` in the unpacked client to convert the client database to `cdclient.sqlite` * Use `fdb_to_sqlite.py` in lcdr's utilities on `res/cdclient.fdb` in the unpacked client to convert the client database to `cdclient.sqlite`
* Move and rename `cdclient.sqlite` into `build/res/CDServer.sqlite` * Move and rename `cdclient.sqlite` into `build/res/CDServer.sqlite`
* Run each SQL file in the order at which they appear [here](migrations/cdserver/) on the SQLite database * Run each SQL file in the order at which they appear [here](migrations/cdserver/) on the SQLite database
@ -205,13 +205,16 @@ Darkflame Universe utilizes a MySQL/MariaDB database for account and character i
Initial setup can vary drastically based on which operating system or distribution you are running; there are instructions out there for most setups, follow those and come back here when you have a database up and running. Initial setup can vary drastically based on which operating system or distribution you are running; there are instructions out there for most setups, follow those and come back here when you have a database up and running.
* Create a database for Darkflame Universe to use * Create a database for Darkflame Universe to use
* Use the command `./MasterServer -m` to automatically run them.
**Configuration** #### Configuration
After the server has been built there should be four `ini` files in the build director: `authconfig.ini`, `chatconfig.ini`, `masterconfig.ini`, and `worldconfig.ini`. Go through them and fill in the database credentials and configure other settings if necessary. After the server has been built there should be four `ini` files in the build director: `authconfig.ini`, `chatconfig.ini`, `masterconfig.ini`, and `worldconfig.ini`. Go through them and fill in the database credentials and configure other settings if necessary.
**Verify** #### Setup and Migrations
Use the command `./MasterServer -m` to setup the tables in the database. The first time this command is run on a database, the tables will be up to date with the most recent version. To update your database tables, run this command again. Multiple invocations will not affect any functionality.
#### Verify
Your build directory should now look like this: Your build directory should now look like this:
* AuthServer * AuthServer

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.offsetGroupID = tableData.getIntField(19, -1);
entry.buildTypes = tableData.getIntField(20, -1); entry.buildTypes = tableData.getIntField(20, -1);
entry.reqPrecondition = tableData.getStringField(21, ""); entry.reqPrecondition = tableData.getStringField(21, "");
entry.animationFlag = tableData.getIntField(22, -1); entry.animationFlag = tableData.getIntField(22, 0);
entry.equipEffects = tableData.getIntField(23, -1); entry.equipEffects = tableData.getIntField(23, -1);
entry.readyForQA = tableData.getIntField(24, -1) == 1 ? true : false; entry.readyForQA = tableData.getIntField(24, -1) == 1 ? true : false;
entry.itemRating = tableData.getIntField(25, -1); entry.itemRating = tableData.getIntField(25, -1);
@ -123,7 +123,7 @@ const CDItemComponent& CDItemComponentTable::GetItemComponentByID(unsigned int s
entry.offsetGroupID = tableData.getIntField(19, -1); entry.offsetGroupID = tableData.getIntField(19, -1);
entry.buildTypes = tableData.getIntField(20, -1); entry.buildTypes = tableData.getIntField(20, -1);
entry.reqPrecondition = tableData.getStringField(21, ""); entry.reqPrecondition = tableData.getStringField(21, "");
entry.animationFlag = tableData.getIntField(22, -1); entry.animationFlag = tableData.getIntField(22, 0);
entry.equipEffects = tableData.getIntField(23, -1); entry.equipEffects = tableData.getIntField(23, -1);
entry.readyForQA = tableData.getIntField(24, -1) == 1 ? true : false; entry.readyForQA = tableData.getIntField(24, -1) == 1 ? true : false;
entry.itemRating = tableData.getIntField(25, -1); 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)); Kill(EntityManager::Instance()->GetEntity(source));
return; 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); destroyableComponent->Smash(source, killType, deathType);
} }

View File

@ -167,18 +167,6 @@ public:
*/ */
void SetLastRocketItemID(LWOOBJID lastRocketItemID) { m_LastRocketItemID = lastRocketItemID; } 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 * Gets the name of this character
* @return the name of this character * @return the name of this character
@ -569,11 +557,6 @@ private:
* ID of the last rocket used * ID of the last rocket used
*/ */
LWOOBJID m_LastRocketItemID = LWOOBJID_EMPTY; LWOOBJID m_LastRocketItemID = LWOOBJID_EMPTY;
/**
* Mount Item ID
*/
LWOOBJID m_MountItemID = LWOOBJID_EMPTY;
}; };
#endif // CHARACTERCOMPONENT_H #endif // CHARACTERCOMPONENT_H

View File

@ -32,6 +32,7 @@ ControllablePhysicsComponent::ControllablePhysicsComponent(Entity* entity) : Com
m_IgnoreMultipliers = false; m_IgnoreMultipliers = false;
m_PickupRadius = 0.0f; m_PickupRadius = 0.0f;
m_DirtyPickupRadiusScale = true; m_DirtyPickupRadiusScale = true;
m_IsTeleporting = false;
if (entity->GetLOT() != 1) // Other physics entities we care about will be added by BaseCombatAI if (entity->GetLOT() != 1) // Other physics entities we care about will be added by BaseCombatAI
return; return;
@ -128,7 +129,10 @@ void ControllablePhysicsComponent::Serialize(RakNet::BitStream* outBitStream, bo
outBitStream->Write0(); outBitStream->Write0();
} }
if (!bIsInitialUpdate) outBitStream->Write0(); if (!bIsInitialUpdate) {
outBitStream->Write(m_IsTeleporting);
m_IsTeleporting = false;
}
} }
void ControllablePhysicsComponent::LoadFromXml(tinyxml2::XMLDocument* doc) { void ControllablePhysicsComponent::LoadFromXml(tinyxml2::XMLDocument* doc) {

View File

@ -220,6 +220,18 @@ public:
*/ */
bool GetStatic() const { return m_Static; } 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 * Returns the Physics entity for the component
* @return Physics entity for the component * @return Physics entity for the component
@ -355,6 +367,11 @@ private:
* The active pickup radius for this entity * The active pickup radius for this entity
*/ */
float m_PickupRadius; float m_PickupRadius;
/**
* If the entity is teleporting
*/
bool m_IsTeleporting;
}; };
#endif // CONTROLLABLEPHYSICSCOMPONENT_H #endif // CONTROLLABLEPHYSICSCOMPONENT_H

View File

@ -26,6 +26,8 @@
#include "MissionComponent.h" #include "MissionComponent.h"
#include "CharacterComponent.h" #include "CharacterComponent.h"
#include "PossessableComponent.h"
#include "PossessorComponent.h"
#include "dZoneManager.h" #include "dZoneManager.h"
DestroyableComponent::DestroyableComponent(Entity* parent) : Component(parent) { DestroyableComponent::DestroyableComponent(Entity* parent) : Component(parent) {
@ -608,6 +610,24 @@ void DestroyableComponent::Damage(uint32_t damage, const LWOOBJID source, uint32
SetHealth(health); SetHealth(health);
SetIsShielded(absorb > 0); 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) { if (m_Parent->GetLOT() != 1) {
echo = true; echo = true;
} }

View File

@ -26,6 +26,7 @@
#include "DestroyableComponent.h" #include "DestroyableComponent.h"
#include "dConfig.h" #include "dConfig.h"
#include "eItemType.h" #include "eItemType.h"
#include "eUnequippableActiveType.h"
InventoryComponent::InventoryComponent(Entity* parent, tinyxml2::XMLDocument* document) : Component(parent) { InventoryComponent::InventoryComponent(Entity* parent, tinyxml2::XMLDocument* document) : Component(parent) {
this->m_Dirty = true; this->m_Dirty = true;
@ -795,9 +796,7 @@ void InventoryComponent::RemoveSlot(const std::string& location) {
} }
void InventoryComponent::EquipItem(Item* item, const bool skipChecks) { void InventoryComponent::EquipItem(Item* item, const bool skipChecks) {
if (!Inventory::IsValidItem(item->GetLot())) { if (!Inventory::IsValidItem(item->GetLot())) return;
return;
}
// Temp items should be equippable but other transfer items shouldn't be (for example the instruments in RB) // Temp items should be equippable but other transfer items shouldn't be (for example the instruments in RB)
if (item->IsEquipped() if (item->IsEquipped()
@ -820,9 +819,7 @@ void InventoryComponent::EquipItem(Item* item, const bool skipChecks) {
auto* characterComponent = m_Parent->GetComponent<CharacterComponent>(); auto* characterComponent = m_Parent->GetComponent<CharacterComponent>();
if (characterComponent != nullptr) { if (characterComponent != nullptr) characterComponent->SetLastRocketItemID(item->GetId());
characterComponent->SetLastRocketItemID(item->GetId());
}
lauchPad->OnUse(m_Parent); 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); 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); if (!building && (item->GetLot() == 6086 || type == eItemType::ITEM_TYPE_LOOT_MODEL || type == eItemType::ITEM_TYPE_VEHICLE)) return;
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 (type != eItemType::ITEM_TYPE_LOOT_MODEL && type != eItemType::ITEM_TYPE_MODEL) { if (type != eItemType::ITEM_TYPE_LOOT_MODEL && type != eItemType::ITEM_TYPE_MODEL) {
if (!item->GetBound() && !item->GetPreconditionExpression()->Check(m_Parent)) { if (!item->GetBound() && !item->GetPreconditionExpression()->Check(m_Parent)) {
@ -940,9 +851,7 @@ void InventoryComponent::EquipItem(Item* item, const bool skipChecks) {
set->OnEquip(lot); set->OnEquip(lot);
} }
if (item->GetInfo().isBOE) { if (item->GetInfo().isBOE) item->SetBound(true);
item->SetBound(true);
}
GenerateProxies(item); 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 { void InventoryComponent::ApplyBuff(Item* item) const {
const auto buffs = FindBuffs(item, true); const auto buffs = FindBuffs(item, true);

View File

@ -198,6 +198,13 @@ public:
*/ */
void UnEquipItem(Item* item); 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 * Adds a buff related to equipping a lot to the entity
* @param item the item to find buffs for * @param item the item to find buffs for

View File

@ -14,6 +14,7 @@
#include "dpWorld.h" #include "dpWorld.h"
#include "PetDigServer.h" #include "PetDigServer.h"
#include "../dWorldServer/ObjectIDManager.h" #include "../dWorldServer/ObjectIDManager.h"
#include "eUnequippableActiveType.h"
#include "Game.h" #include "Game.h"
#include "dConfig.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::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(); activePets[m_Owner] = m_Parent->GetObjectID();
@ -945,7 +946,7 @@ void PetComponent::AddDrainImaginationTimer(Item* item, bool fromTaming) {
void PetComponent::Deactivate() { void PetComponent::Deactivate() {
GameMessages::SendPlayFXEffect(m_Parent->GetObjectID(), -1, u"despawn", "", LWOOBJID_EMPTY, 1, 1, true); 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); 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 // Should a result not exist for this default to attached visible
if (!result.eof()) { 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)); m_DepossessOnHit = static_cast<bool>(result.getIntField(1, 0));
} else { } else {
m_PossessionType = ePossessionType::ATTACHED_VISIBLE; 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) { void PossessableComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags) {
outBitStream->Write(m_DirtyPossessable || bIsInitialUpdate); outBitStream->Write(m_DirtyPossessable || bIsInitialUpdate);
if (m_DirtyPossessable || bIsInitialUpdate) { if (m_DirtyPossessable || bIsInitialUpdate) {
m_DirtyPossessable = false; m_DirtyPossessable = false; // reset flag
outBitStream->Write(m_Possessor != LWOOBJID_EMPTY); outBitStream->Write(m_Possessor != LWOOBJID_EMPTY);
if (m_Possessor != LWOOBJID_EMPTY) outBitStream->Write(m_Possessor); 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); if (m_AnimationFlag != eAnimationFlags::IDLE_INVALID) outBitStream->Write(m_AnimationFlag);
outBitStream->Write(m_ImmediatelyDepossess); outBitStream->Write(m_ImmediatelyDepossess);
m_ImmediatelyDepossess = false; // reset flag
} }
} }
void PossessableComponent::OnUse(Entity* originator) { void PossessableComponent::Dismount() {
// TODO: Implement this 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); 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 * @param value the ID of the possessor to set
*/ */
void SetPossessor(LWOOBJID value) { m_Possessor = value; m_DirtyPossessable = true; }; void SetPossessor(LWOOBJID value) { m_Possessor = value; m_DirtyPossessable = true; };
/** /**
* Returns the possessor of this entity * Returns the possessor of this Entity
* @return the possessor of this entity * @return the possessor of this Entity
*/ */
LWOOBJID GetPossessor() const { return m_Possessor; }; LWOOBJID GetPossessor() const { return m_Possessor; };
@ -38,19 +48,19 @@ public:
void SetAnimationFlag(eAnimationFlags value) { m_AnimationFlag = value; m_DirtyPossessable = true; }; void SetAnimationFlag(eAnimationFlags value) { m_AnimationFlag = value; m_DirtyPossessable = true; };
/** /**
* Returns the possession type of this entity * Returns the possession type of this Entity
* @return the possession type of this entity * @return the possession type of this Entity
*/ */
ePossessionType GetPossessionType() const { return m_PossessionType; }; ePossessionType GetPossessionType() const { return m_PossessionType; };
/** /**
* Returns if the entity should deposses on hit * Returns if the Entity should deposses on hit
* @return if the entity should deposses on hit * @return if the Entity should deposses on hit
*/ */
bool GetDepossessOnHit() const { return m_DepossessOnHit; }; bool GetDepossessOnHit() const { return m_DepossessOnHit; };
/** /**
* Forcibly depossess the entity * Forcibly depossess the Entity
*/ */
void ForceDepossess() { m_ImmediatelyDepossess = true; m_DirtyPossessable = true; }; void ForceDepossess() { m_ImmediatelyDepossess = true; m_DirtyPossessable = true; };
@ -58,13 +68,13 @@ public:
* Set if the parent entity was spawned from an item * Set if the parent entity was spawned from an item
* @param value 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 * Returns if the parent entity was spawned from an item
* @return 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 * 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 "PossessorComponent.h"
#include "PossessableComponent.h"
#include "CharacterComponent.h"
#include "EntityManager.h"
#include "GameMessages.h"
#include "eUnequippableActiveType.h"
PossessorComponent::PossessorComponent(Entity* parent) : Component(parent) { PossessorComponent::PossessorComponent(Entity* parent) : Component(parent) {
m_Possessable = LWOOBJID_EMPTY; 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) { void PossessorComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags) {
outBitStream->Write(m_DirtyPossesor || bIsInitialUpdate); outBitStream->Write(m_DirtyPossesor || bIsInitialUpdate);
@ -19,3 +35,48 @@ void PossessorComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInit
outBitStream->Write(m_PossessableType); 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); void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags);
/** /**
* Sets the entity that this entity is possessing * @brief Mounts the entity
* @param value the ID of the entity this ID should posess *
* @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; } void SetPossessable(LWOOBJID value) { m_Possessable = value; m_DirtyPossesor = true; }
/** /**
* Returns the entity that this entity is currently posessing * 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; } LWOOBJID GetPossessable() const { return m_Possessable; }
/** /**
* Sets if we are busy mounting or dismounting * Sets if we are busy dismounting
* @param value if we are busy mounting or 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 * Returns if we are busy dismounting
* @return if we are busy mounting or 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 * 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; } 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: private:
/** /**
@ -68,14 +96,19 @@ private:
ePossessionType m_PossessableType = ePossessionType::NO_POSSESSION; ePossessionType m_PossessableType = ePossessionType::NO_POSSESSION;
/** /**
* @brief if the possessor is dirty * @brief If the possessor is dirty
* *
*/ */
bool m_DirtyPossesor = false; 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); GameMessages::HandleUpdatePlayerStatistic(inStream, entity);
break; break;
case GAME_MSG_DISMOUNT_COMPLETE:
GameMessages::HandleDismountComplete(inStream, entity, sysAddr);
break;
default: default:
//Game::logger->Log("GameMessageHandler", "Unknown game message ID: %X", messageID); //Game::logger->Log("GameMessageHandler", "Unknown game message ID: %X", messageID);
break; break;

View File

@ -27,6 +27,7 @@
#include "ChatPackets.h" #include "ChatPackets.h"
#include "GameConfig.h" #include "GameConfig.h"
#include "RocketLaunchLupComponent.h" #include "RocketLaunchLupComponent.h"
#include "eUnequippableActiveType.h"
#include <sstream> #include <sstream>
#include <future> #include <future>
@ -3367,7 +3368,7 @@ void GameMessages::SendRegisterPetDBID(LWOOBJID objectId, LWOOBJID petDBID, cons
SEND_PACKET; 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; CBITSTREAM;
CMSGHEADER; CMSGHEADER;
@ -3376,8 +3377,8 @@ void GameMessages::SendMarkInventoryItemAsActive(LWOOBJID objectId, bool bActive
bitStream.Write(bActive); bitStream.Write(bActive);
bitStream.Write(iType != 0); bitStream.Write(iType != eUnequippableActiveType::INVALID);
if (iType != 0) bitStream.Write(iType); if (iType != eUnequippableActiveType::INVALID) bitStream.Write(iType);
bitStream.Write(itemID != LWOOBJID_EMPTY); bitStream.Write(itemID != LWOOBJID_EMPTY);
if (itemID != LWOOBJID_EMPTY) bitStream.Write(itemID); if (itemID != LWOOBJID_EMPTY) bitStream.Write(itemID);
@ -3814,7 +3815,6 @@ void GameMessages::SendDisplayChatBubble(LWOOBJID objectId, const std::u16string
void GameMessages::SendSetMountInventoryID(Entity* entity, const LWOOBJID& objectID, const SystemAddress& sysAddr) { void GameMessages::SendSetMountInventoryID(Entity* entity, const LWOOBJID& objectID, const SystemAddress& sysAddr) {
CBITSTREAM; CBITSTREAM;
CMSGHEADER; CMSGHEADER;
bitStream.Write(entity->GetObjectID()); bitStream.Write(entity->GetObjectID());
bitStream.Write(GAME_MSG::GAME_MSG_SET_MOUNT_INVENTORY_ID); bitStream.Write(GAME_MSG::GAME_MSG_SET_MOUNT_INVENTORY_ID);
bitStream.Write(objectID); bitStream.Write(objectID);
@ -3824,30 +3824,53 @@ void GameMessages::SendSetMountInventoryID(Entity* entity, const LWOOBJID& objec
void GameMessages::HandleDismountComplete(RakNet::BitStream* inStream, Entity* entity, const SystemAddress& sysAddr) { void GameMessages::HandleDismountComplete(RakNet::BitStream* inStream, Entity* entity, const SystemAddress& sysAddr) {
// Get the objectID from the bitstream
LWOOBJID objectId{}; LWOOBJID objectId{};
inStream->Read(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) { if (objectId != LWOOBJID_EMPTY) {
PossessorComponent* possessor; auto* possessorComponent = entity->GetComponent<PossessorComponent>();
if (entity->TryGetComponent(COMPONENT_TYPE_POSSESSOR, possessor)) { auto* mount = EntityManager::Instance()->GetEntity(objectId);
if (mount) { // make sure we have the things we need and they aren't null
possessor->SetIsBusy(false); if (possessorComponent && mount) {
possessor->SetPossessable(LWOOBJID_EMPTY); if (!possessorComponent->GetIsDismounting()) return;
possessor->SetPossessableType(ePossessionType::NO_POSSESSION); 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); // character related things
auto* character = entity->GetComponent<CharacterComponent>();
EntityManager::Instance()->SerializeEntity(entity); 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) { 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); EntityManager::Instance()->SerializeEntity(entity);
LWOOBJID objectId{};
inStream->Read(objectId);
auto* mount = EntityManager::Instance()->GetEntity(objectId);
if (mount) EntityManager::Instance()->SerializeEntity(mount);
} }
//Racing //Racing
@ -4698,12 +4721,6 @@ void GameMessages::HandleFireEventServerSide(RakNet::BitStream* inStream, Entity
return; return;
} }
if (args == u"toggleMail") {
AMFArrayValue args;
args.InsertValue("visible", new AMFFalseValue());
GameMessages::SendUIMessageServerToSingleClient(entity, sysAddr, "ToggleMail", &args);
}
// This should probably get it's own "ServerEvents" system or something at some point // This should probably get it's own "ServerEvents" system or something at some point
if (args == u"ZonePlayer") { if (args == u"ZonePlayer") {
// Should probably check to make sure they're using a launcher at some point before someone makes a hack that lets you testmap // Should probably check to make sure they're using a launcher at some point before someone makes a hack that lets you testmap

View File

@ -19,6 +19,7 @@ class NiQuaternion;
class User; class User;
class Entity; class Entity;
class NiPoint3; class NiPoint3;
enum class eUnequippableActiveType;
namespace GameMessages { namespace GameMessages {
class PropertyDataMessage; class PropertyDataMessage;
@ -318,7 +319,7 @@ namespace GameMessages {
void SendRegisterPetDBID(LWOOBJID objectId, LWOOBJID petDBID, const SystemAddress& sysAddr); 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); void SendClientExitTamingMinigame(LWOOBJID objectId, bool bVoluntaryExit, const SystemAddress& sysAddr);

View File

@ -10,6 +10,9 @@
#include "dLogger.h" #include "dLogger.h"
#include "EntityManager.h" #include "EntityManager.h"
#include "RenderComponent.h" #include "RenderComponent.h"
#include "PossessableComponent.h"
#include "CharacterComponent.h"
#include "eItemType.h"
class Inventory; class Inventory;
@ -71,6 +74,12 @@ Item::Item(
id = GeneralUtils::SetBit(id, OBJECT_BIT_CHARACTER); id = GeneralUtils::SetBit(id, OBJECT_BIT_CHARACTER);
id = GeneralUtils::SetBit(id, OBJECT_BIT_PERSISTENT); 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; this->id = id;
inventory->AddManagedItem(this); inventory->AddManagedItem(this);
@ -254,49 +263,34 @@ bool Item::Consume() {
return success; 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"); auto* compRegistryTable = CDClientManager::Instance()->GetTable<CDComponentsRegistryTable>("ComponentsRegistry");
const auto packageComponentId = compRegistryTable->GetByIDAndType(lot, COMPONENT_TYPE_PACKAGE); const auto packageComponentId = compRegistryTable->GetByIDAndType(lot, COMPONENT_TYPE_PACKAGE);
auto* packCompTable = CDClientManager::Instance()->GetTable<CDPackageComponentTable>("PackageComponent"); auto* packCompTable = CDClientManager::Instance()->GetTable<CDPackageComponentTable>("PackageComponent");
auto packages = packCompTable->Query([=](const CDPackageComponent entry) {return entry.id == static_cast<uint32_t>(packageComponentId); }); auto packages = packCompTable->Query([=](const CDPackageComponent entry) {return entry.id == static_cast<uint32_t>(packageComponentId); });
const auto success = !packages.empty(); const auto success = !packages.empty();
if (success) {
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))) {
auto* entityParent = inventory->GetComponent()->GetParent(); auto* entityParent = inventory->GetComponent()->GetParent();
for (auto& pack : packages) { for (auto& pack : packages) {
std::unordered_map<LOT, int32_t> result{}; std::unordered_map<LOT, int32_t> result{};
result = LootGenerator::Instance().RollLootMatrix(entityParent, pack.LootMatrixIndex); result = LootGenerator::Instance().RollLootMatrix(entityParent, pack.LootMatrixIndex);
if (!inventory->GetComponent()->HasSpaceForLoot(result)) { if (!inventory->GetComponent()->HasSpaceForLoot(result)) {
return false;
} }
LootGenerator::Instance().GiveLoot(inventory->GetComponent()->GetParent(), result, eLootSourceType::LOOT_SOURCE_CONSUMPTION); LootGenerator::Instance().GiveLoot(inventory->GetComponent()->GetParent(), result, eLootSourceType::LOOT_SOURCE_CONSUMPTION);
} }
Game::logger->Log("Item", "Used (%i)", lot);
inventory->GetComponent()->RemoveItem(lot, 1); inventory->GetComponent()->RemoveItem(lot, 1);
} }
}
return success;
} }
void Item::Disassemble(const eInventoryType inventoryType) { 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 * 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 * 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) { if ((chatCommand == "playanimation" || chatCommand == "playanim") && args.size() == 1 && entity->GetGMLevel() >= GAME_MASTER_LEVEL_DEVELOPER) {
std::u16string anim = GeneralUtils::ASCIIToUTF16(args[0], args[0].size()); std::u16string anim = GeneralUtils::ASCIIToUTF16(args[0], args[0].size());
GameMessages::SendPlayAnimation(entity, anim); 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) { 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>(); auto* controllablePhysicsComponent = entity->GetComponent<ControllablePhysicsComponent>();
if (controllablePhysicsComponent == nullptr) { if (!controllablePhysicsComponent) return;
return;
}
controllablePhysicsComponent->SetSpeedMultiplier(boost); 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); 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)."); 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()); auto* possassableEntity = EntityManager::Instance()->GetEntity(possessorComponent->GetPossessable());
if (possassableEntity != nullptr) { if (possassableEntity != nullptr) {
auto* vehiclePhysicsComponent = possassableEntity->GetComponent<VehiclePhysicsComponent>(); auto* vehiclePhysicsComponent = possassableEntity->GetComponent<VehiclePhysicsComponent>();
if (vehiclePhysicsComponent) {
if (vehiclePhysicsComponent != nullptr) {
vehiclePhysicsComponent->SetPosition(pos); vehiclePhysicsComponent->SetPosition(pos);
EntityManager::Instance()->SerializeEntity(possassableEntity); EntityManager::Instance()->SerializeEntity(possassableEntity);
} else GameMessages::SendTeleport(possassableEntity->GetObjectID(), pos, NiQuaternion(), sysAddr);
Game::logger->Log("ClientPackets", "Forced updated vehicle position");
}
} }
} }
} }
@ -926,18 +939,12 @@ void SlashCommandHandler::HandleChatCommand(const std::u16string& command, Entit
} }
if (chatCommand == "dismount" && entity->GetGMLevel() >= GAME_MASTER_LEVEL_DEVELOPER) { if (chatCommand == "dismount" && entity->GetGMLevel() >= GAME_MASTER_LEVEL_DEVELOPER) {
PossessorComponent* possessorComponent; auto* possessorComponent = entity->GetComponent<PossessorComponent>();
if (entity->TryGetComponent(COMPONENT_TYPE_POSSESSOR, possessorComponent)) { if (possessorComponent) {
Entity* vehicle = EntityManager::Instance()->GetEntity(possessorComponent->GetPossessable()); auto possessableId = possessorComponent->GetPossessable();
if (!vehicle) return; if (possessableId != LWOOBJID_EMPTY) {
auto* possessableEntity = EntityManager::Instance()->GetEntity(possessableId);
PossessableComponent* possessableComponent; if (possessableEntity) possessorComponent->Dismount(possessableEntity, true);
if (vehicle->TryGetComponent(COMPONENT_TYPE_POSSESSABLE, possessableComponent)) {
possessableComponent->SetPossessor(LWOOBJID_EMPTY);
possessorComponent->SetPossessable(LWOOBJID_EMPTY);
EntityManager::Instance()->SerializeEntity(vehicle);
EntityManager::Instance()->SerializeEntity(entity);
} }
} }
} }
@ -1231,6 +1238,18 @@ void SlashCommandHandler::HandleChatCommand(const std::u16string& command, Entit
return; 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); EntityManager::Instance()->ConstructEntity(newEntity);
} }
@ -1520,7 +1539,33 @@ void SlashCommandHandler::HandleChatCommand(const std::u16string& command, Entit
return; 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); 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) { 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); inStream.Read(angVelocity.z);
} }
bool hasVehicle = false; bool updateChar = true;
if (possessorComponent != nullptr) { if (possessorComponent != nullptr) {
auto* possassableEntity = EntityManager::Instance()->GetEntity(possessorComponent->GetPossessable()); auto* possassableEntity = EntityManager::Instance()->GetEntity(possessorComponent->GetPossessable());
if (possassableEntity != nullptr) { 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) { if (vehiclePhysicsComponent != nullptr) {
// This is flipped for whatever reason // This is flipped for whatever reason
rotation = NiQuaternion(rotation.z, rotation.y, rotation.x, rotation.w); 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->SetDirtyVelocity(velocityFlag);
vehiclePhysicsComponent->SetAngularVelocity(angVelocity); vehiclePhysicsComponent->SetAngularVelocity(angVelocity);
vehiclePhysicsComponent->SetDirtyAngularVelocity(angVelocityFlag); 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); EntityManager::Instance()->SerializeEntity(possassableEntity);
hasVehicle = true;
}
} }
} }
if (hasVehicle) { if (!updateChar) {
velocity = NiPoint3::ZERO; velocity = NiPoint3::ZERO;
angVelocity = NiPoint3::ZERO; angVelocity = NiPoint3::ZERO;
} }
// Handle statistics // Handle statistics
auto* characterComponent = entity->GetComponent<CharacterComponent>(); auto* characterComponent = entity->GetComponent<CharacterComponent>();
if (characterComponent != nullptr) { if (characterComponent != nullptr) {
@ -192,9 +208,7 @@ void ClientPackets::HandleClientPositionUpdate(const SystemAddress& sysAddr, Pac
player->SetGhostReferencePoint(position); player->SetGhostReferencePoint(position);
EntityManager::Instance()->QueueGhostUpdate(player->GetObjectID()); EntityManager::Instance()->QueueGhostUpdate(player->GetObjectID());
if (!hasVehicle) { if (updateChar) EntityManager::Instance()->SerializeEntity(entity);
EntityManager::Instance()->SerializeEntity(entity);
}
//TODO: add moving platform stuffs //TODO: add moving platform stuffs
/*bool movingPlatformFlag; /*bool movingPlatformFlag;

15
dScripts/AgQbWall.cpp Normal file
View File

@ -0,0 +1,15 @@
#include "AgQbWall.h"
void AgQbWall::OnRebuildComplete(Entity* self, Entity* player) {
self->SetVar(u"player", player->GetObjectID());
auto targetWallSpawners = GeneralUtils::UTF16ToWTF8(self->GetVar<std::u16string>(u"spawner"));
if (targetWallSpawners != "") {
auto groupObjs = EntityManager::Instance()->GetEntitiesInGroup(targetWallSpawners);
for (auto* obj : groupObjs) {
if (obj) {
obj->SetVar(u"player", player->GetObjectID());
obj->OnFireEventServerSide(self, "spawnMobs");
}
}
}
}

7
dScripts/AgQbWall.h Normal file
View File

@ -0,0 +1,7 @@
#pragma once
#include "CppScripts.h"
class AgQbWall : public CppScripts::Script {
public:
void OnRebuildComplete(Entity* self, Entity* player) override;
};

View File

@ -21,6 +21,7 @@ set(DSCRIPT_SOURCES "ActivityManager.cpp"
"AgPropGuard.cpp" "AgPropGuard.cpp"
"AgPropguards.cpp" "AgPropguards.cpp"
"AgQbElevator.cpp" "AgQbElevator.cpp"
"AgQbWall.cpp"
"AgSalutingNpcs.cpp" "AgSalutingNpcs.cpp"
"AgShipPlayerDeathTrigger.cpp" "AgShipPlayerDeathTrigger.cpp"
"AgShipPlayerShockServer.cpp" "AgShipPlayerShockServer.cpp"
@ -94,11 +95,13 @@ set(DSCRIPT_SOURCES "ActivityManager.cpp"
"FvConsoleRightQuickbuild.cpp" "FvConsoleRightQuickbuild.cpp"
"FvDragonSmashingGolemQb.cpp" "FvDragonSmashingGolemQb.cpp"
"FvFacilityBrick.cpp" "FvFacilityBrick.cpp"
"FvFacilityPipes.cpp"
"FvFlyingCreviceDragon.cpp" "FvFlyingCreviceDragon.cpp"
"FvFong.cpp" "FvFong.cpp"
"FvFreeGfNinjas.cpp" "FvFreeGfNinjas.cpp"
"FvHorsemenTrigger.cpp" "FvHorsemenTrigger.cpp"
"FvMaelstromCavalry.cpp" "FvMaelstromCavalry.cpp"
"FvMaelstromGeyser.cpp"
"FvMaelstromDragon.cpp" "FvMaelstromDragon.cpp"
"FvNinjaGuard.cpp" "FvNinjaGuard.cpp"
"FvPandaServer.cpp" "FvPandaServer.cpp"
@ -113,7 +116,9 @@ set(DSCRIPT_SOURCES "ActivityManager.cpp"
"GfCaptainsCannon.cpp" "GfCaptainsCannon.cpp"
"GfJailkeepMission.cpp" "GfJailkeepMission.cpp"
"GfJailWalls.cpp" "GfJailWalls.cpp"
"GfMaelstromGeyser.cpp"
"GfOrgan.cpp" "GfOrgan.cpp"
"GfParrotCrash.cpp"
"GfTikiTorch.cpp" "GfTikiTorch.cpp"
"GrowingFlower.cpp" "GrowingFlower.cpp"
"HydrantBroken.cpp" "HydrantBroken.cpp"
@ -193,6 +198,7 @@ set(DSCRIPT_SOURCES "ActivityManager.cpp"
"PetDigServer.cpp" "PetDigServer.cpp"
"PetFromDigServer.cpp" "PetFromDigServer.cpp"
"PetFromObjectServer.cpp" "PetFromObjectServer.cpp"
"PirateRep.cpp"
"PropertyBankInteract.cpp" "PropertyBankInteract.cpp"
"PropertyDeathPlane.cpp" "PropertyDeathPlane.cpp"
"PropertyDevice.cpp" "PropertyDevice.cpp"
@ -201,6 +207,7 @@ set(DSCRIPT_SOURCES "ActivityManager.cpp"
"PrSeagullFly.cpp" "PrSeagullFly.cpp"
"PrWhistle.cpp" "PrWhistle.cpp"
"QbEnemyStunner.cpp" "QbEnemyStunner.cpp"
"QbSpawner.cpp"
"RaceImagineCrateServer.cpp" "RaceImagineCrateServer.cpp"
"RaceImaginePowerup.cpp" "RaceImaginePowerup.cpp"
"RaceMaelstromGeiser.cpp" "RaceMaelstromGeiser.cpp"
@ -240,6 +247,7 @@ set(DSCRIPT_SOURCES "ActivityManager.cpp"
"WaveBossHammerling.cpp" "WaveBossHammerling.cpp"
"WaveBossHorsemen.cpp" "WaveBossHorsemen.cpp"
"WaveBossSpiderling.cpp" "WaveBossSpiderling.cpp"
"WblGenericZone.cpp"
"WhFans.cpp" "WhFans.cpp"
"WildAmbients.cpp" "WildAmbients.cpp"
"WishingWellServer.cpp" "WishingWellServer.cpp"

View File

@ -61,6 +61,8 @@
#include "VeMissionConsole.h" #include "VeMissionConsole.h"
#include "VeEpsilonServer.h" #include "VeEpsilonServer.h"
#include "AgSurvivalBuffStation.h" #include "AgSurvivalBuffStation.h"
#include "QbSpawner.h"
#include "AgQbWall.h"
// NS Scripts // NS Scripts
#include "NsModularBuild.h" #include "NsModularBuild.h"
@ -117,6 +119,9 @@
#include "GfApeSmashingQB.h" #include "GfApeSmashingQB.h"
#include "ZoneGfProperty.h" #include "ZoneGfProperty.h"
#include "GfArchway.h" #include "GfArchway.h"
#include "GfMaelstromGeyser.h"
#include "PirateRep.h"
#include "GfParrotCrash.h"
// SG Scripts // SG Scripts
#include "SGCannon.h" #include "SGCannon.h"
@ -141,12 +146,14 @@
#include "FvConsoleLeftQuickbuild.h" #include "FvConsoleLeftQuickbuild.h"
#include "FvConsoleRightQuickbuild.h" #include "FvConsoleRightQuickbuild.h"
#include "FvFacilityBrick.h" #include "FvFacilityBrick.h"
#include "FvFacilityPipes.h"
#include "ImgBrickConsoleQB.h" #include "ImgBrickConsoleQB.h"
#include "ActParadoxPipeFix.h" #include "ActParadoxPipeFix.h"
#include "FvNinjaGuard.h" #include "FvNinjaGuard.h"
#include "FvPassThroughWall.h" #include "FvPassThroughWall.h"
#include "FvBounceOverWall.h" #include "FvBounceOverWall.h"
#include "FvFong.h" #include "FvFong.h"
#include "FvMaelstromGeyser.h"
// FB Scripts // FB Scripts
#include "AgJetEffectServer.h" #include "AgJetEffectServer.h"
@ -281,6 +288,9 @@
#include "RockHydrantBroken.h" #include "RockHydrantBroken.h"
#include "WhFans.h" #include "WhFans.h"
// WBL scripts
#include "WblGenericZone.h"
//Big bad global bc this is a namespace and not a class: //Big bad global bc this is a namespace and not a class:
InvalidScript* invalidToReturn = new InvalidScript(); InvalidScript* invalidToReturn = new InvalidScript();
std::map<std::string, CppScripts::Script*> m_Scripts; std::map<std::string, CppScripts::Script*> m_Scripts;
@ -461,6 +471,10 @@ CppScripts::Script* CppScripts::GetScript(Entity* parent, const std::string& scr
script = new WildAmbients(); script = new WildAmbients();
else if (scriptName == "scripts\\ai\\NS\\NS_PP_01\\L_NS_PP_01_TELEPORT.lua") else if (scriptName == "scripts\\ai\\NS\\NS_PP_01\\L_NS_PP_01_TELEPORT.lua")
script = new PropertyDeathPlane(); script = new PropertyDeathPlane();
else if (scriptName == "scripts\\02_server\\Map\\General\\L_QB_SPAWNER.lua")
script = new QbSpawner();
else if (scriptName == "scripts\\ai\\AG\\L_AG_QB_Wall.lua")
script = new AgQbWall();
//GF: //GF:
else if (scriptName == "scripts\\02_server\\Map\\GF\\L_GF_TORCH.lua") else if (scriptName == "scripts\\02_server\\Map\\GF\\L_GF_TORCH.lua")
@ -495,9 +509,14 @@ CppScripts::Script* CppScripts::GetScript(Entity* parent, const std::string& scr
script = new GfApeSmashingQB(); script = new GfApeSmashingQB();
else if (scriptName == "scripts\\zone\\PROPERTY\\GF\\L_ZONE_GF_PROPERTY.lua") else if (scriptName == "scripts\\zone\\PROPERTY\\GF\\L_ZONE_GF_PROPERTY.lua")
script = new ZoneGfProperty(); script = new ZoneGfProperty();
else if (scriptName == "scripts\\ai\\GF\\L_GF_ARCHWAY.lua") { else if (scriptName == "scripts\\ai\\GF\\L_GF_ARCHWAY.lua")
script = new GfArchway(); script = new GfArchway();
} else if (scriptName == "scripts\\ai\\GF\\L_GF_MAELSTROM_GEYSER.lua")
script = new GfMaelstromGeyser();
else if (scriptName == "scripts\\ai\\GF\\L_PIRATE_REP.lua")
script = new PirateRep();
else if (scriptName == "scripts\\ai\\GF\\L_GF_PARROT_CRASH.lua")
script = new GfParrotCrash();
// SG // SG
else if (scriptName == "scripts\\ai\\MINIGAME\\SG_GF\\SERVER\\SG_CANNON.lua") else if (scriptName == "scripts\\ai\\MINIGAME\\SG_GF\\SERVER\\SG_CANNON.lua")
@ -564,6 +583,8 @@ CppScripts::Script* CppScripts::GetScript(Entity* parent, const std::string& scr
script = new FvConsoleRightQuickbuild(); script = new FvConsoleRightQuickbuild();
else if (scriptName == "scripts\\ai\\FV\\L_FV_FACILITY_BRICK.lua") else if (scriptName == "scripts\\ai\\FV\\L_FV_FACILITY_BRICK.lua")
script = new FvFacilityBrick(); script = new FvFacilityBrick();
else if (scriptName == "scripts\\ai\\FV\\L_FV_FACILITY_PIPES.lua")
script = new FvFacilityPipes();
else if (scriptName == "scripts\\02_server\\Map\\FV\\L_IMG_BRICK_CONSOLE_QB.lua") else if (scriptName == "scripts\\02_server\\Map\\FV\\L_IMG_BRICK_CONSOLE_QB.lua")
script = new ImgBrickConsoleQB(); script = new ImgBrickConsoleQB();
else if (scriptName == "scripts\\ai\\FV\\L_ACT_PARADOX_PIPE_FIX.lua") else if (scriptName == "scripts\\ai\\FV\\L_ACT_PARADOX_PIPE_FIX.lua")
@ -576,6 +597,9 @@ CppScripts::Script* CppScripts::GetScript(Entity* parent, const std::string& scr
script = new FvBounceOverWall(); script = new FvBounceOverWall();
else if (scriptName == "scripts\\02_server\\Map\\FV\\L_NPC_FONG.lua") else if (scriptName == "scripts\\02_server\\Map\\FV\\L_NPC_FONG.lua")
script = new FvFong(); script = new FvFong();
else if (scriptName == "scripts\\ai\\FV\\L_FV_MAELSTROM_GEYSER.lua") {
script = new FvMaelstromGeyser();
}
//Misc: //Misc:
if (scriptName == "scripts\\02_server\\Map\\General\\L_EXPLODING_ASSET.lua") if (scriptName == "scripts\\02_server\\Map\\General\\L_EXPLODING_ASSET.lua")
@ -813,12 +837,17 @@ CppScripts::Script* CppScripts::GetScript(Entity* parent, const std::string& scr
script = new BuccaneerValiantShip(); script = new BuccaneerValiantShip();
else if (scriptName == "scripts\\EquipmentScripts\\FireFirstSkillonStartup.lua") else if (scriptName == "scripts\\EquipmentScripts\\FireFirstSkillonStartup.lua")
script = new FireFirstSkillonStartup(); script = new FireFirstSkillonStartup();
// FB // FB
else if (scriptName == "scripts\\ai\\NS\\WH\\L_ROCKHYDRANT_BROKEN.lua") else if (scriptName == "scripts\\ai\\NS\\WH\\L_ROCKHYDRANT_BROKEN.lua")
script = new RockHydrantBroken(); script = new RockHydrantBroken();
else if (scriptName == "scripts\\ai\\NS\\L_NS_WH_FANS.lua") else if (scriptName == "scripts\\ai\\NS\\L_NS_WH_FANS.lua")
script = new WhFans(); script = new WhFans();
// WBL
else if (scriptName == "scripts\\zone\\LUPs\\WBL_generic_zone.lua")
script = new WblGenericZone();
//Ignore these scripts: //Ignore these scripts:
else if (scriptName == "scripts\\02_server\\Enemy\\General\\L_SUSPEND_LUA_AI.lua") else if (scriptName == "scripts\\02_server\\Enemy\\General\\L_SUSPEND_LUA_AI.lua")
script = invalidToReturn; script = invalidToReturn;

View File

@ -0,0 +1,10 @@
#include "FvFacilityPipes.h"
#include "GameMessages.h"
void FvFacilityPipes::OnFireEventServerSide(Entity* self, Entity* sender, std::string args, int32_t param1, int32_t param2, int32_t param3) {
if (args == "startFX") {
GameMessages::SendPlayFXEffect(self->GetObjectID(), m_LeftPipeEffectID, m_EffectType, m_LeftPipeEffectName);
GameMessages::SendPlayFXEffect(self->GetObjectID(), m_RightPipeEffectID, m_EffectType, m_RightPipeEffectName);
GameMessages::SendPlayFXEffect(self->GetObjectID(), m_ImaginationCanisterEffectID, m_EffectType, m_ImaginationCanisterEffectName);
}
}

View File

@ -0,0 +1,15 @@
#pragma once
#include "CppScripts.h"
class FvFacilityPipes : public CppScripts::Script {
public:
void OnFireEventServerSide(Entity* self, Entity* sender, std::string args, int32_t param1, int32_t param2, int32_t param3) override;
private:
const std::u16string m_EffectType = u"create";
const std::string m_LeftPipeEffectName = "LeftPipeOff";
const int32_t m_LeftPipeEffectID = 2774;
const std::string m_RightPipeEffectName = "RightPipeOff";
const int32_t m_RightPipeEffectID = 2777;
const std::string m_ImaginationCanisterEffectName = "imagination_canister";
const int32_t m_ImaginationCanisterEffectID = 2750;
};

View File

@ -0,0 +1,18 @@
#include "FvMaelstromGeyser.h"
#include "SkillComponent.h"
void FvMaelstromGeyser::OnStartup(Entity* self) {
self->AddTimer(m_StartSkillTimerName, m_StartSkillTimerTime);
self->AddTimer(m_KillSelfTimerName, m_KillSelfTimerTime);
}
void FvMaelstromGeyser::OnTimerDone(Entity* self, std::string timerName) {
if (timerName == m_StartSkillTimerName) {
auto* skillComponent = self->GetComponent<SkillComponent>();
skillComponent->CalculateBehavior(m_SkillID, m_BehaviorID, LWOOBJID_EMPTY, true);
}
if (timerName == m_KillSelfTimerName) {
self->Smash(LWOOBJID_EMPTY, eKillType::SILENT);
}
}

View File

@ -0,0 +1,18 @@
#pragma once
#include "CppScripts.h"
class FvMaelstromGeyser final : public CppScripts::Script
{
public:
void OnStartup(Entity* self) override;
void OnTimerDone(Entity* self, std::string timerName) override;
private:
const std::string m_StartSkillTimerName = "startSkill";
const float m_StartSkillTimerTime = 2.0;
const std::string m_KillSelfTimerName = "killSelf";
const float m_KillSelfTimerTime = 5.5;
const uint32_t m_SkillID = 831;
const uint32_t m_BehaviorID = 15500;
};

View File

@ -0,0 +1,18 @@
#include "GfMaelstromGeyser.h"
#include "SkillComponent.h"
void GfMaelstromGeyser::OnStartup(Entity* self) {
self->AddTimer(m_StartSkillTimerName, m_StartSkillTimerTime);
self->AddTimer(m_KillSelfTimerName, m_KillSelfTimerTime);
}
void GfMaelstromGeyser::OnTimerDone(Entity* self, std::string timerName) {
if (timerName == m_StartSkillTimerName) {
auto* skillComponent = self->GetComponent<SkillComponent>();
skillComponent->CalculateBehavior(m_SkillID, m_BehaviorID, LWOOBJID_EMPTY, true);
}
if (timerName == m_KillSelfTimerName) {
self->Smash(LWOOBJID_EMPTY, eKillType::SILENT);
}
}

View File

@ -0,0 +1,17 @@
#pragma once
#include "CppScripts.h"
class GfMaelstromGeyser final : public CppScripts::Script
{
public:
void OnStartup(Entity* self) override;
void OnTimerDone(Entity* self, std::string timerName) override;
private:
const std::string m_StartSkillTimerName = "startSkill";
const float m_StartSkillTimerTime = 2.0;
const std::string m_KillSelfTimerName = "killSelf";
const float m_KillSelfTimerTime = 7.5;
const uint32_t m_SkillID = 607;
const uint32_t m_BehaviorID = 10500;
};

View File

@ -0,0 +1,13 @@
#include "GfParrotCrash.h"
#include "SkillComponent.h"
#include "Entity.h"
#include "dLogger.h"
void GfParrotCrash::OnFireEventServerSide(Entity* self, Entity* sender, std::string args, int32_t param1, int32_t param2, int32_t param3) {
auto* skillComponent = self->GetComponent<SkillComponent>();
if (args == "Slow") {
skillComponent->CalculateBehavior(m_SlowSkillID, m_SlowBehaviorID, sender->GetObjectID());
} else if (args == "Unslow") {
skillComponent->CalculateBehavior(m_UnslowSkillID, m_UnslowBehaviorID, sender->GetObjectID());
}
}

13
dScripts/GfParrotCrash.h Normal file
View File

@ -0,0 +1,13 @@
#pragma once
#include "CppScripts.h"
class GfParrotCrash : public CppScripts::Script {
public:
void OnFireEventServerSide(Entity* self, Entity* sender, std::string args, int32_t param1, int32_t param2, int32_t param3) override;
private:
const uint32_t m_SlowSkillID = 795;
const uint32_t m_SlowBehaviorID = 14214;
const uint32_t m_UnslowSkillID = 796;
const uint32_t m_UnslowBehaviorID = 14215;
};

View File

@ -9,3 +9,11 @@ void MailBoxServer::OnUse(Entity* self, Entity* user) {
args.InsertValue("state", value); args.InsertValue("state", value);
GameMessages::SendUIMessageServerToSingleClient(user, user->GetSystemAddress(), "pushGameState", &args); GameMessages::SendUIMessageServerToSingleClient(user, user->GetSystemAddress(), "pushGameState", &args);
} }
void MailBoxServer::OnFireEventServerSide(Entity* self, Entity* sender, std::string args, int32_t param1, int32_t param2, int32_t param3) {
if (args == "toggleMail") {
AMFArrayValue args;
args.InsertValue("visible", new AMFFalseValue());
GameMessages::SendUIMessageServerToSingleClient(sender, sender->GetSystemAddress(), "ToggleMail", &args);
}
}

View File

@ -12,4 +12,5 @@ public:
* @param user The user that interacted with this Entity. * @param user The user that interacted with this Entity.
*/ */
void OnUse(Entity* self, Entity* user) override; void OnUse(Entity* self, Entity* user) override;
void OnFireEventServerSide(Entity* self, Entity* sender, std::string args, int32_t param1, int32_t param2, int32_t param3) override;
}; };

11
dScripts/PirateRep.cpp Normal file
View File

@ -0,0 +1,11 @@
#include "PirateRep.h"
#include "Character.h"
void PirateRep::OnMissionDialogueOK(Entity* self, Entity* target, int missionID, MissionState missionState) {
if (missionID == m_PirateRepMissionID && missionState >= MissionState::MISSION_STATE_READY_TO_COMPLETE) {
auto* character = target->GetCharacter();
if (character) {
character->SetPlayerFlag(ePlayerFlags::GF_PIRATE_REP, true);
}
}
}

9
dScripts/PirateRep.h Normal file
View File

@ -0,0 +1,9 @@
#pragma once
#include "CppScripts.h"
class PirateRep : public CppScripts::Script {
public:
void OnMissionDialogueOK(Entity* self, Entity* target, int missionID, MissionState missionState) override;
private:
const int m_PirateRepMissionID = 301;
};

136
dScripts/QbSpawner.cpp Normal file
View File

@ -0,0 +1,136 @@
#include "QbSpawner.h"
#include "BaseCombatAIComponent.h"
#include "MovementAIComponent.h"
void QbSpawner::OnStartup(Entity* self) {
auto mobNum = self->GetVar<int>(u"mobNum");
auto spawnDist = self->GetVar<float>(u"spawnDist");
auto mobTemplate = self->GetVar<LWOOBJID>(u"mobTemplate");
auto spawnTime = self->GetVar<float>(u"spawnTime");
if (!mobNum) self->SetVar<int>(u"mobNum", m_DefaultMobNum);
if (!spawnDist) self->SetVar<float>(u"spawnDist", m_DefaultSpawnDist);
if (!mobTemplate) self->SetVar<LWOOBJID>(u"mobTemplate", m_DefaultMobTemplate);
if (!spawnTime) self->SetVar<float>(u"spawnTime", m_DefaultSpawnTime);
// go ahead and setup the mob table here
std::vector<LWOOBJID> mobTable;
mobTable.assign(self->GetVar<int>(u"mobNum"), LWOOBJID_EMPTY);
self->SetVar<std::vector<LWOOBJID>>(u"mobTable", mobTable);
}
void QbSpawner::OnFireEventServerSide(Entity* self, Entity* sender, std::string args, int32_t param1, int32_t param2, int32_t param3) {
auto gateObjID = sender->GetObjectID();
if (!gateObjID) return;
if (args == "spawnMobs") {
self->SetVar(u"gateObj", gateObjID);
auto spawnTime = self->GetVar<float>(u"spawnTime");
self->AddTimer("SpawnMobEnemies", spawnTime);
}
}
void QbSpawner::OnTimerDone(Entity* self, std::string timerName) {
if (timerName == "SpawnMobEnemies") {
auto mobTable = self->GetVar<std::vector<LWOOBJID>>(u"mobTable");
auto spawnDist = self->GetVar<float>(u"spawnDist");
auto mobTemplate = self->GetVar<LWOOBJID>(u"mobTemplate");
auto gateObjID = self->GetVar<LWOOBJID>(u"gateObj");
if (!gateObjID) return;
auto* gate = EntityManager::Instance()->GetEntity(gateObjID);
if (!gate) return;
auto oPos = gate->GetPosition();
auto oDir = gate->GetRotation().GetForwardVector();
NiPoint3 newPos(
oPos.x + (oDir.x * spawnDist),
oPos.y,
oPos.z + (oDir.z * spawnDist)
);
auto newRot = NiQuaternion::LookAt(newPos, oPos);
for (int i = 0; i < mobTable.size(); i++) {
int posOffset = -10;
if (mobTable[i] == LWOOBJID_EMPTY) {
posOffset = posOffset + 5 * i;
auto newOffset = newPos;
newOffset.z = newOffset.z + posOffset;
EntityInfo info{};
info.lot = mobTemplate;
info.pos = newOffset;
info.rot = newRot;
info.spawnerID = self->GetObjectID();
info.spawnerNodeID = 0;
info.settings = {
new LDFData<bool>(u"no_timed_spawn", true),
new LDFData<float>(u"aggroRadius", 70),
new LDFData<float>(u"softtetherRadius", 80),
new LDFData<float>(u"tetherRadius", 90),
new LDFData<float>(u"wanderRadius", 5),
new LDFData<int>(u"mobTableLoc", i)
};
auto* child = EntityManager::Instance()->CreateEntity(info, nullptr, self);
EntityManager::Instance()->ConstructEntity(child);
OnChildLoaded(self, child);
} else {
auto* mob = EntityManager::Instance()->GetEntity(mobTable[i]);
AggroTargetObject(self, mob);
}
}
}
}
void QbSpawner::OnChildLoaded(Entity* self, Entity* child) {
auto mobTable = self->GetVar<std::vector<LWOOBJID>>(u"mobTable");
auto tableLoc = child->GetVar<int>(u"mobTableLoc");
mobTable[tableLoc] = child->GetObjectID();
self->SetVar<std::vector<LWOOBJID>>(u"mobTable", mobTable);
AggroTargetObject(self, child);
const auto selfID = self->GetObjectID();
child->AddDieCallback([this, selfID, child]() {
auto* self = EntityManager::Instance()->GetEntity(selfID);
OnChildRemoved(self, child);
}
);
}
void QbSpawner::OnChildRemoved(Entity* self, Entity* child) {
auto mobTable = self->GetVar<std::vector<LWOOBJID>>(u"mobTable");
auto tableLoc = child->GetVar<int>(u"mobTableLoc");
mobTable[tableLoc] = LWOOBJID_EMPTY;
self->SetVar<std::vector<LWOOBJID>>(u"mobTable", mobTable);
}
void QbSpawner::AggroTargetObject(Entity* self, Entity* enemy) {
auto* baseCombatAIComponent = enemy->GetComponent<BaseCombatAIComponent>();
if (!baseCombatAIComponent) return;
auto gateObjID = self->GetVar<LWOOBJID>(u"gateObj");
if (gateObjID) {
auto* gate = EntityManager::Instance()->GetEntity(gateObjID);
if (gate) {
auto* movementAIComponent = enemy->GetComponent<MovementAIComponent>();
if (movementAIComponent) movementAIComponent->SetDestination(gate->GetPosition());
baseCombatAIComponent->Taunt(gateObjID, 1000);
}
}
auto playerObjID = self->GetVar<LWOOBJID>(u"player");
if (playerObjID) {
baseCombatAIComponent->Taunt(playerObjID, 100);
}
}

17
dScripts/QbSpawner.h Normal file
View File

@ -0,0 +1,17 @@
#pragma once
#include "CppScripts.h"
class QbSpawner : public CppScripts::Script {
public:
void OnStartup(Entity* self) override;
void OnFireEventServerSide(Entity* self, Entity* sender, std::string args, int32_t param1, int32_t param2, int32_t param3) override;
void OnTimerDone(Entity* self, std::string timerName) override;
void OnChildLoaded(Entity* self, Entity* child);
void OnChildRemoved(Entity* self, Entity* child);
void AggroTargetObject(Entity* self, Entity* enemy);
private:
const int m_DefaultMobNum = 3;
const float m_DefaultSpawnDist = 25.0;
const LWOOBJID m_DefaultMobTemplate = 4712;
const float m_DefaultSpawnTime = 2.0;
};

View File

@ -0,0 +1,10 @@
#include "WblGenericZone.h"
#include "Player.h"
void WblGenericZone::OnFireEventServerSide(Entity* self, Entity* sender, std::string args, int32_t param1, int32_t param2, int32_t param3) {
if (args == m_WblAbortMsg) {
if (!sender) return;
auto player = dynamic_cast<Player*>(sender);
if (player) player->SendToZone(m_WblMainZone);
}
}

10
dScripts/WblGenericZone.h Normal file
View File

@ -0,0 +1,10 @@
#pragma once
#include "CppScripts.h"
class WblGenericZone : public CppScripts::Script
{
public:
void OnFireEventServerSide(Entity* self, Entity* sender, std::string args, int32_t param1, int32_t param2, int32_t param3) override;
private:
const LWOMAPID m_WblMainZone = 1600;
const std::string m_WblAbortMsg = "AbortWBLZone";
};

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| |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| |activatespawner|`/activatespawner <spawner name>`|Activates spawner by name.|8|
|addmission|`/addmission <mission id>`|Accepts the mission, adding it to your journal.|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| |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| |buffme|`/buffme`|Sets health, armor, and imagination to 999.|8|
|buffmed|`/buffmed`|Sets health, armor, and imagination to 9.|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| |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| |createprivate|`/createprivate <zone id> <clone id> <password>`|Creates a private zone with password.|8|
|debugui|`/debugui`|Toggle Debug UI.|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| |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| |freecam|`/freecam`|Toggles freecam mode.|8|
|freemoney|`/freemoney <coins>`|Gives coins.|8| |freemoney|`/freemoney <coins>`|Gives coins.|8|

View File

@ -6,13 +6,15 @@ set(tinyxml2_BUILD_TESTING OFF)
# Source Code for tinyxml2 # Source Code for tinyxml2
add_subdirectory(tinyxml2) add_subdirectory(tinyxml2)
# Source Code for libbcrypt # Source Code for libbcrypt. Uses a file glob instead to get around Windows build issues.
# Disable warning about no project version. file(
set(CMAKE_POLICY_DEFAULT_CMP0048 NEW) GLOB SOURCES_LIBBCRYPT
# Disable warning about the minimum version of cmake used for bcrypt being deprecated in the future LIST_DIRECTORIES false
set(CMAKE_WARN_DEPRECATED OFF CACHE BOOL "" FORCE) RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}"
${CMAKE_CURRENT_SOURCE_DIR}/libbcrypt/*.c
add_subdirectory(libbcrypt) ${CMAKE_CURRENT_SOURCE_DIR}/libbcrypt/src/*.c
)
add_library(bcrypt ${SOURCES_LIBBCRYPT})
# Source code for sqlite # Source code for sqlite
add_subdirectory(SQLite) add_subdirectory(SQLite)