mirror of
https://github.com/DarkflameUniverse/DarkflameServer.git
synced 2025-08-05 18:24:12 +00:00
Merge branch 'main' into main
This commit is contained in:
@@ -191,7 +191,7 @@ void BaseCombatAIComponent::Update(const float deltaTime) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_Stunned || m_SkillTime > 0) {
|
||||
if (m_Stunned) {
|
||||
m_MovementAI->Stop();
|
||||
|
||||
return;
|
||||
@@ -357,7 +357,7 @@ void BaseCombatAIComponent::CalculateCombat(const float deltaTime) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_Downtime = 0.5f; // TODO: find out if this is necessary
|
||||
m_Downtime = 0.5f;
|
||||
|
||||
auto* target = GetTargetEntity();
|
||||
|
||||
|
@@ -51,7 +51,7 @@ public:
|
||||
|
||||
void LoadFromXML(tinyxml2::XMLDocument* doc);
|
||||
|
||||
void UpdateXml(tinyxml2::XMLDocument* doc);
|
||||
void UpdateXml(tinyxml2::XMLDocument* doc) override;
|
||||
|
||||
void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags);
|
||||
|
||||
|
@@ -12,6 +12,8 @@
|
||||
#include "EntityManager.h"
|
||||
#include "PossessorComponent.h"
|
||||
#include "VehiclePhysicsComponent.h"
|
||||
#include "GameMessages.h"
|
||||
#include "Item.h"
|
||||
|
||||
CharacterComponent::CharacterComponent(Entity* parent, Character* character) : Component(parent) {
|
||||
m_Character = character;
|
||||
@@ -31,6 +33,7 @@ CharacterComponent::CharacterComponent(Entity* parent, Character* character) : C
|
||||
|
||||
m_EditorEnabled = false;
|
||||
m_EditorLevel = m_GMLevel;
|
||||
m_Reputation = 0;
|
||||
|
||||
m_CurrentActivity = 0;
|
||||
m_CountryCode = 0;
|
||||
@@ -42,7 +45,7 @@ CharacterComponent::CharacterComponent(Entity* parent, Character* character) : C
|
||||
if (character->GetZoneID() != Game::server->GetZoneID()) {
|
||||
m_IsLanding = true;
|
||||
}
|
||||
|
||||
|
||||
if (LandingAnimDisabled(character->GetZoneID()) || LandingAnimDisabled(Game::server->GetZoneID()) || m_LastRocketConfig.empty()) {
|
||||
m_IsLanding = false; //Don't make us land on VE/minigames lol
|
||||
}
|
||||
@@ -191,6 +194,7 @@ void CharacterComponent::HandleLevelUp()
|
||||
auto* rewardsTable = CDClientManager::Instance()->GetTable<CDRewardsTable>("Rewards");
|
||||
|
||||
const auto& rewards = rewardsTable->GetByLevelID(m_Level);
|
||||
bool rewardingItem = rewards.size() > 0;
|
||||
|
||||
auto* parent = m_Character->GetEntity();
|
||||
|
||||
@@ -206,37 +210,34 @@ void CharacterComponent::HandleLevelUp()
|
||||
{
|
||||
return;
|
||||
}
|
||||
// Tell the client we beginning to send level rewards.
|
||||
if(rewardingItem) GameMessages::NotifyLevelRewards(parent->GetObjectID(), parent->GetSystemAddress(), m_Level, rewardingItem);
|
||||
|
||||
for (auto* reward : rewards)
|
||||
{
|
||||
switch (reward->rewardType)
|
||||
{
|
||||
case 0:
|
||||
inventoryComponent->AddItem(reward->value, reward->count);
|
||||
inventoryComponent->AddItem(reward->value, reward->count, eLootSourceType::LOOT_SOURCE_LEVEL_REWARD);
|
||||
break;
|
||||
|
||||
case 4:
|
||||
{
|
||||
auto* items = inventoryComponent->GetInventory(ITEMS);
|
||||
auto* items = inventoryComponent->GetInventory(eInventoryType::ITEMS);
|
||||
items->SetSize(items->GetSize() + reward->value);
|
||||
}
|
||||
break;
|
||||
|
||||
case 9:
|
||||
controllablePhysicsComponent->SetSpeedMultiplier(static_cast<float>(reward->value) / 500.0f);
|
||||
break;
|
||||
|
||||
case 11:
|
||||
break;
|
||||
|
||||
case 12:
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
// Tell the client we have finished sending level rewards.
|
||||
if(rewardingItem) GameMessages::NotifyLevelRewards(parent->GetObjectID(), parent->GetSystemAddress(), m_Level, !rewardingItem);
|
||||
}
|
||||
|
||||
void CharacterComponent::SetGMLevel(int gmlevel) {
|
||||
@@ -257,7 +258,10 @@ void CharacterComponent::LoadFromXML() {
|
||||
Game::logger->Log("CharacterComponent", "Failed to find char tag while loading XML!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (character->QueryAttribute("rpt", &m_Reputation) == tinyxml2::XML_NO_ATTRIBUTE) {
|
||||
SetReputation(0);
|
||||
}
|
||||
|
||||
character->QueryInt64Attribute("ls", &m_Uscore);
|
||||
|
||||
// Load the statistics
|
||||
@@ -379,6 +383,8 @@ void CharacterComponent::UpdateXml(tinyxml2::XMLDocument* doc) {
|
||||
}
|
||||
|
||||
character->SetAttribute("ls", m_Uscore);
|
||||
// Custom attribute to keep track of reputation.
|
||||
character->SetAttribute("rpt", GetReputation());
|
||||
character->SetAttribute("stt", StatisticsToString().c_str());
|
||||
|
||||
// Set the zone statistics of the form <zs><s/> ... <s/></zs>
|
||||
@@ -443,6 +449,56 @@ void CharacterComponent::SetLastRocketConfig(std::u16string config) {
|
||||
m_LastRocketConfig = config;
|
||||
}
|
||||
|
||||
Item* CharacterComponent::GetRocket(Entity* player) {
|
||||
Item* rocket = nullptr;
|
||||
|
||||
auto* inventoryComponent = player->GetComponent<InventoryComponent>();
|
||||
|
||||
if (!inventoryComponent) return rocket;
|
||||
|
||||
// Select the rocket
|
||||
if (!rocket){
|
||||
rocket = inventoryComponent->FindItemById(GetLastRocketItemID());
|
||||
}
|
||||
|
||||
if (!rocket) {
|
||||
rocket = inventoryComponent->FindItemByLot(6416);
|
||||
}
|
||||
|
||||
if (!rocket) {
|
||||
Game::logger->Log("CharacterComponent", "Unable to find rocket to equip!\n");
|
||||
return rocket;
|
||||
}
|
||||
return rocket;
|
||||
}
|
||||
|
||||
Item* CharacterComponent::RocketEquip(Entity* player) {
|
||||
Item* rocket = GetRocket(player);
|
||||
if (!rocket) return rocket;
|
||||
|
||||
// build and define the rocket config
|
||||
for (LDFBaseData* data : rocket->GetConfig()) {
|
||||
if (data->GetKey() == u"assemblyPartLOTs") {
|
||||
std::string newRocketStr = data->GetValueAsString() + ";";
|
||||
GeneralUtils::ReplaceInString(newRocketStr, "+", ";");
|
||||
SetLastRocketConfig(GeneralUtils::ASCIIToUTF16(newRocketStr));
|
||||
}
|
||||
}
|
||||
|
||||
// Store the last used rocket item's ID
|
||||
SetLastRocketItemID(rocket->GetId());
|
||||
// carry the rocket
|
||||
rocket->Equip(true);
|
||||
return rocket;
|
||||
}
|
||||
|
||||
void CharacterComponent::RocketUnEquip(Entity* player) {
|
||||
Item* rocket = GetRocket(player);
|
||||
if (!rocket) return;
|
||||
// We don't want to carry it anymore
|
||||
rocket->UnEquip();
|
||||
}
|
||||
|
||||
void CharacterComponent::TrackMissionCompletion(bool isAchievement) {
|
||||
UpdatePlayerStatistic(MissionsCompleted);
|
||||
|
||||
|
@@ -5,6 +5,7 @@
|
||||
#include "RakNetTypes.h"
|
||||
#include "Character.h"
|
||||
#include "Component.h"
|
||||
#include "Item.h"
|
||||
#include <string>
|
||||
#include "CDMissionsTable.h"
|
||||
#include "tinyxml2.h"
|
||||
@@ -74,6 +75,26 @@ public:
|
||||
*/
|
||||
void SetLastRocketConfig(std::u16string config);
|
||||
|
||||
/**
|
||||
* Find a player's rocket
|
||||
* @param player the entity that triggered the event
|
||||
* @return rocket
|
||||
*/
|
||||
Item* GetRocket(Entity* player);
|
||||
|
||||
/**
|
||||
* Equip a player's rocket
|
||||
* @param player the entity that triggered the event
|
||||
* @return rocket
|
||||
*/
|
||||
Item* RocketEquip(Entity* player);
|
||||
|
||||
/**
|
||||
* Find a player's rocket and unequip it
|
||||
* @param player the entity that triggered the event
|
||||
*/
|
||||
void RocketUnEquip(Entity* player);
|
||||
|
||||
/**
|
||||
* Gets the current level of the entity
|
||||
* @return the current level of the entity
|
||||
@@ -146,6 +167,18 @@ public:
|
||||
*/
|
||||
bool GetPvpEnabled() const;
|
||||
|
||||
/**
|
||||
* Returns the characters lifetime reputation
|
||||
* @return The lifetime reputation of this character.
|
||||
*/
|
||||
int64_t GetReputation() { return m_Reputation; };
|
||||
|
||||
/**
|
||||
* Sets the lifetime reputation of the character to newValue
|
||||
* @param newValue the value to set reputation to
|
||||
*/
|
||||
void SetReputation(int64_t newValue) { m_Reputation = newValue; };
|
||||
|
||||
/**
|
||||
* Sets the current value of PvP combat being enabled
|
||||
* @param value whether to enable PvP combat
|
||||
@@ -291,6 +324,11 @@ private:
|
||||
*/
|
||||
int64_t m_Uscore;
|
||||
|
||||
/**
|
||||
* The lifetime reputation earned by the entity
|
||||
*/
|
||||
int64_t m_Reputation;
|
||||
|
||||
/**
|
||||
* Whether the character is landing by rocket
|
||||
*/
|
||||
|
@@ -29,6 +29,8 @@ ControllablePhysicsComponent::ControllablePhysicsComponent(Entity* entity) : Com
|
||||
m_GravityScale = 1;
|
||||
m_DirtyCheats = false;
|
||||
m_IgnoreMultipliers = false;
|
||||
m_PickupRadius = 0.0f;
|
||||
m_DirtyPickupRadiusScale = true;
|
||||
|
||||
if (entity->GetLOT() != 1) // Other physics entities we care about will be added by BaseCombatAI
|
||||
return;
|
||||
@@ -85,7 +87,13 @@ void ControllablePhysicsComponent::Serialize(RakNet::BitStream* outBitStream, bo
|
||||
m_DirtyCheats = false;
|
||||
}
|
||||
|
||||
outBitStream->Write0();
|
||||
outBitStream->Write(m_DirtyPickupRadiusScale);
|
||||
if (m_DirtyPickupRadiusScale) {
|
||||
outBitStream->Write(m_PickupRadius);
|
||||
outBitStream->Write0(); //No clue what this is so im leaving it false.
|
||||
m_DirtyPickupRadiusScale = false;
|
||||
}
|
||||
|
||||
outBitStream->Write0();
|
||||
|
||||
outBitStream->Write(m_DirtyPosition || bIsInitialUpdate);
|
||||
@@ -230,4 +238,32 @@ void ControllablePhysicsComponent::SetDirtyVelocity(bool val) {
|
||||
|
||||
void ControllablePhysicsComponent::SetDirtyAngularVelocity(bool val) {
|
||||
m_DirtyAngularVelocity = val;
|
||||
}
|
||||
}
|
||||
|
||||
void ControllablePhysicsComponent::AddPickupRadiusScale(float value) {
|
||||
m_ActivePickupRadiusScales.push_back(value);
|
||||
if (value > m_PickupRadius) {
|
||||
m_PickupRadius = value;
|
||||
m_DirtyPickupRadiusScale = true;
|
||||
}
|
||||
}
|
||||
|
||||
void ControllablePhysicsComponent::RemovePickupRadiusScale(float value) {
|
||||
// Attempt to remove pickup radius from active radii
|
||||
const auto pos = std::find(m_ActivePickupRadiusScales.begin(), m_ActivePickupRadiusScales.end(), value);
|
||||
if (pos != m_ActivePickupRadiusScales.end()) {
|
||||
m_ActivePickupRadiusScales.erase(pos);
|
||||
} else {
|
||||
Game::logger->Log("ControllablePhysicsComponent", "Warning: Could not find pickup radius %f in list of active radii. List has %i active radii.\n", value, m_ActivePickupRadiusScales.size());
|
||||
return;
|
||||
}
|
||||
|
||||
// Recalculate pickup radius since we removed one by now
|
||||
m_PickupRadius = 0.0f;
|
||||
m_DirtyPickupRadiusScale = true;
|
||||
for (uint32_t i = 0; i < m_ActivePickupRadiusScales.size(); i++) {
|
||||
auto candidateRadius = m_ActivePickupRadiusScales[i];
|
||||
if (m_PickupRadius < candidateRadius) m_PickupRadius = candidateRadius;
|
||||
}
|
||||
EntityManager::Instance()->SerializeEntity(m_Parent);
|
||||
}
|
||||
|
@@ -23,7 +23,7 @@ public:
|
||||
ControllablePhysicsComponent(Entity* entity);
|
||||
~ControllablePhysicsComponent() override;
|
||||
|
||||
void Update(float deltaTime);
|
||||
void Update(float deltaTime) override;
|
||||
void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags);
|
||||
void LoadFromXML(tinyxml2::XMLDocument* doc);
|
||||
void ResetFlags();
|
||||
@@ -227,6 +227,24 @@ public:
|
||||
|
||||
dpEntity* GetdpEntity() const { return m_dpEntity; }
|
||||
|
||||
/**
|
||||
* I store this in a vector because if I have 2 separate pickup radii being applied to the player, I dont know which one is correctly active.
|
||||
* This method adds the pickup radius to the vector of active radii and if its larger than the current one, is applied as the new pickup radius.
|
||||
*/
|
||||
void AddPickupRadiusScale(float value) ;
|
||||
|
||||
/**
|
||||
* Removes the provided pickup radius scale from our list of buffs
|
||||
* The recalculates what our pickup radius is.
|
||||
*/
|
||||
void RemovePickupRadiusScale(float value) ;
|
||||
|
||||
/**
|
||||
* The pickup radii of this component.
|
||||
* @return All active radii scales for this component.
|
||||
*/
|
||||
std::vector<float> GetActivePickupRadiusScales() { return m_ActivePickupRadiusScales; };
|
||||
|
||||
private:
|
||||
/**
|
||||
* The entity that owns this component
|
||||
@@ -322,6 +340,21 @@ private:
|
||||
* Whether this entity is static, making it unable to move
|
||||
*/
|
||||
bool m_Static;
|
||||
|
||||
/**
|
||||
* Whether the pickup scale is dirty.
|
||||
*/
|
||||
bool m_DirtyPickupRadiusScale;
|
||||
|
||||
/**
|
||||
* The list of pickup radius scales for this entity
|
||||
*/
|
||||
std::vector<float> m_ActivePickupRadiusScales;
|
||||
|
||||
/**
|
||||
* The active pickup radius for this entity
|
||||
*/
|
||||
float m_PickupRadius;
|
||||
};
|
||||
|
||||
#endif // CONTROLLABLEPHYSICSCOMPONENT_H
|
||||
|
@@ -26,6 +26,7 @@
|
||||
|
||||
#include "MissionComponent.h"
|
||||
#include "CharacterComponent.h"
|
||||
#include "dZoneManager.h"
|
||||
|
||||
DestroyableComponent::DestroyableComponent(Entity* parent) : Component(parent) {
|
||||
m_iArmor = 0;
|
||||
@@ -214,6 +215,8 @@ void DestroyableComponent::SetHealth(int32_t value) {
|
||||
|
||||
void DestroyableComponent::SetMaxHealth(float value, bool playAnim) {
|
||||
m_DirtyHealth = true;
|
||||
// Used for playAnim if opted in for.
|
||||
int32_t difference = static_cast<int32_t>(std::abs(m_fMaxHealth - value));
|
||||
m_fMaxHealth = value;
|
||||
|
||||
if (m_iHealth > m_fMaxHealth) {
|
||||
@@ -224,27 +227,28 @@ void DestroyableComponent::SetMaxHealth(float value, bool playAnim) {
|
||||
// Now update the player bar
|
||||
if (!m_Parent->GetParentUser()) return;
|
||||
AMFStringValue* amount = new AMFStringValue();
|
||||
amount->SetStringValue(std::to_string(value));
|
||||
amount->SetStringValue(std::to_string(difference));
|
||||
AMFStringValue* type = new AMFStringValue();
|
||||
type->SetStringValue("health");
|
||||
|
||||
AMFArrayValue args;
|
||||
args.InsertValue("amount", amount);
|
||||
args.InsertValue("type", type);
|
||||
|
||||
GameMessages::SendUIMessageServerToSingleClient(m_Parent, m_Parent->GetParentUser()->GetSystemAddress(), "MaxPlayerBarUpdate", &args);
|
||||
|
||||
delete amount;
|
||||
delete type;
|
||||
}
|
||||
else {
|
||||
EntityManager::Instance()->SerializeEntity(m_Parent);
|
||||
}
|
||||
|
||||
EntityManager::Instance()->SerializeEntity(m_Parent);
|
||||
}
|
||||
|
||||
void DestroyableComponent::SetArmor(int32_t value) {
|
||||
m_DirtyHealth = true;
|
||||
|
||||
// If Destroyable Component already has zero armor do not trigger the passive ability again.
|
||||
bool hadArmor = m_iArmor > 0;
|
||||
|
||||
auto* characterComponent = m_Parent->GetComponent<CharacterComponent>();
|
||||
if (characterComponent != nullptr) {
|
||||
characterComponent->TrackArmorDelta(value - m_iArmor);
|
||||
@@ -253,7 +257,7 @@ void DestroyableComponent::SetArmor(int32_t value) {
|
||||
m_iArmor = value;
|
||||
|
||||
auto* inventroyComponent = m_Parent->GetComponent<InventoryComponent>();
|
||||
if (m_iArmor == 0 && inventroyComponent != nullptr) {
|
||||
if (m_iArmor == 0 && inventroyComponent != nullptr && hadArmor) {
|
||||
inventroyComponent->TriggerPassiveAbility(PassiveAbilityTrigger::SentinelArmor);
|
||||
}
|
||||
}
|
||||
@@ -283,9 +287,8 @@ void DestroyableComponent::SetMaxArmor(float value, bool playAnim) {
|
||||
delete amount;
|
||||
delete type;
|
||||
}
|
||||
else {
|
||||
EntityManager::Instance()->SerializeEntity(m_Parent);
|
||||
}
|
||||
|
||||
EntityManager::Instance()->SerializeEntity(m_Parent);
|
||||
}
|
||||
|
||||
void DestroyableComponent::SetImagination(int32_t value) {
|
||||
@@ -306,6 +309,8 @@ void DestroyableComponent::SetImagination(int32_t value) {
|
||||
|
||||
void DestroyableComponent::SetMaxImagination(float value, bool playAnim) {
|
||||
m_DirtyHealth = true;
|
||||
// Used for playAnim if opted in for.
|
||||
int32_t difference = static_cast<int32_t>(std::abs(m_fMaxImagination - value));
|
||||
m_fMaxImagination = value;
|
||||
|
||||
if (m_iImagination > m_fMaxImagination) {
|
||||
@@ -316,22 +321,19 @@ void DestroyableComponent::SetMaxImagination(float value, bool playAnim) {
|
||||
// Now update the player bar
|
||||
if (!m_Parent->GetParentUser()) return;
|
||||
AMFStringValue* amount = new AMFStringValue();
|
||||
amount->SetStringValue(std::to_string(value));
|
||||
amount->SetStringValue(std::to_string(difference));
|
||||
AMFStringValue* type = new AMFStringValue();
|
||||
type->SetStringValue("imagination");
|
||||
|
||||
AMFArrayValue args;
|
||||
args.InsertValue("amount", amount);
|
||||
args.InsertValue("type", type);
|
||||
|
||||
GameMessages::SendUIMessageServerToSingleClient(m_Parent, m_Parent->GetParentUser()->GetSystemAddress(), "MaxPlayerBarUpdate", &args);
|
||||
|
||||
delete amount;
|
||||
delete type;
|
||||
}
|
||||
else {
|
||||
EntityManager::Instance()->SerializeEntity(m_Parent);
|
||||
}
|
||||
EntityManager::Instance()->SerializeEntity(m_Parent);
|
||||
}
|
||||
|
||||
void DestroyableComponent::SetDamageToAbsorb(int32_t value)
|
||||
@@ -591,7 +593,7 @@ void DestroyableComponent::Repair(const uint32_t armor)
|
||||
}
|
||||
|
||||
|
||||
void DestroyableComponent::Damage(uint32_t damage, const LWOOBJID source, bool echo)
|
||||
void DestroyableComponent::Damage(uint32_t damage, const LWOOBJID source, uint32_t skillID, bool echo)
|
||||
{
|
||||
if (GetHealth() <= 0)
|
||||
{
|
||||
@@ -675,11 +677,10 @@ void DestroyableComponent::Damage(uint32_t damage, const LWOOBJID source, bool e
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
Smash(source);
|
||||
Smash(source, eKillType::VIOLENT, u"", skillID);
|
||||
}
|
||||
|
||||
void DestroyableComponent::Smash(const LWOOBJID source, const eKillType killType, const std::u16string& deathType)
|
||||
void DestroyableComponent::Smash(const LWOOBJID source, const eKillType killType, const std::u16string& deathType, uint32_t skillID)
|
||||
{
|
||||
if (m_iHealth > 0)
|
||||
{
|
||||
@@ -725,31 +726,20 @@ void DestroyableComponent::Smash(const LWOOBJID source, const eKillType killType
|
||||
if (memberMissions == nullptr) continue;
|
||||
|
||||
memberMissions->Progress(MissionTaskType::MISSION_TASK_TYPE_SMASH, m_Parent->GetLOT());
|
||||
memberMissions->Progress(MissionTaskType::MISSION_TASK_TYPE_SKILL, m_Parent->GetLOT(), skillID);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
missions->Progress(MissionTaskType::MISSION_TASK_TYPE_SMASH, m_Parent->GetLOT());
|
||||
missions->Progress(MissionTaskType::MISSION_TASK_TYPE_SKILL, m_Parent->GetLOT(), skillID);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const auto isPlayer = m_Parent->IsPlayer();
|
||||
|
||||
GameMessages::SendDie(
|
||||
m_Parent,
|
||||
source,
|
||||
source,
|
||||
true,
|
||||
killType,
|
||||
deathType,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
isPlayer,
|
||||
false,
|
||||
1
|
||||
);
|
||||
GameMessages::SendDie(m_Parent, source, source, true, killType, deathType, 0, 0, 0, isPlayer, false, 1);
|
||||
|
||||
//NANI?!
|
||||
if (!isPlayer)
|
||||
@@ -793,32 +783,34 @@ void DestroyableComponent::Smash(const LWOOBJID source, const eKillType killType
|
||||
}
|
||||
else
|
||||
{
|
||||
auto* character = m_Parent->GetCharacter();
|
||||
|
||||
uint64_t coinsTotal = character->GetCoins();
|
||||
|
||||
if (coinsTotal > 0)
|
||||
//Check if this zone allows coin drops
|
||||
if (dZoneManager::Instance()->GetPlayerLoseCoinOnDeath())
|
||||
{
|
||||
uint64_t coinsToLoose = 1;
|
||||
auto* character = m_Parent->GetCharacter();
|
||||
uint64_t coinsTotal = character->GetCoins();
|
||||
|
||||
if (coinsTotal >= 200)
|
||||
if (coinsTotal > 0)
|
||||
{
|
||||
float hundreth = (coinsTotal / 100.0f);
|
||||
coinsToLoose = static_cast<int>(hundreth);
|
||||
uint64_t coinsToLoose = 1;
|
||||
|
||||
if (coinsTotal >= 200)
|
||||
{
|
||||
float hundreth = (coinsTotal / 100.0f);
|
||||
coinsToLoose = static_cast<int>(hundreth);
|
||||
}
|
||||
|
||||
if (coinsToLoose > 10000)
|
||||
{
|
||||
coinsToLoose = 10000;
|
||||
}
|
||||
|
||||
coinsTotal -= coinsToLoose;
|
||||
|
||||
LootGenerator::Instance().DropLoot(m_Parent, m_Parent, -1, coinsToLoose, coinsToLoose);
|
||||
character->SetCoins(coinsTotal, eLootSourceType::LOOT_SOURCE_PICKUP);
|
||||
}
|
||||
|
||||
if (coinsToLoose > 10000)
|
||||
{
|
||||
coinsToLoose = 10000;
|
||||
}
|
||||
|
||||
coinsTotal -= coinsToLoose;
|
||||
|
||||
LootGenerator::Instance().DropLoot(m_Parent, m_Parent, -1, coinsToLoose, coinsToLoose);
|
||||
}
|
||||
|
||||
character->SetCoins(coinsTotal, LOOT_SOURCE_PICKUP);
|
||||
|
||||
Entity* zoneControl = EntityManager::Instance()->GetZoneControlEntity();
|
||||
for (CppScripts::Script* script : CppScripts::GetEntityScripts(zoneControl)) {
|
||||
script->OnPlayerDied(zoneControl, m_Parent);
|
||||
|
@@ -377,17 +377,19 @@ public:
|
||||
* Attempt to damage this entity, handles everything from health and armor to absorption, immunity and callbacks.
|
||||
* @param damage the damage to attempt to apply
|
||||
* @param source the attacker that caused this damage
|
||||
* @param skillID the skill that damaged this entity
|
||||
* @param echo whether or not to serialize the damage
|
||||
*/
|
||||
void Damage(uint32_t damage, LWOOBJID source, bool echo = true);
|
||||
void Damage(uint32_t damage, LWOOBJID source, uint32_t skillID = 0, bool echo = true);
|
||||
|
||||
/**
|
||||
* Smashes this entity, notifying all clients
|
||||
* @param source the source that smashed this entity
|
||||
* @param skillID the skill that killed this entity
|
||||
* @param killType the way this entity was killed, determines if a client animation is played
|
||||
* @param deathType the animation to play when killed
|
||||
*/
|
||||
void Smash(LWOOBJID source, eKillType killType = eKillType::VIOLENT, const std::u16string& deathType = u"");
|
||||
void Smash(LWOOBJID source, eKillType killType = eKillType::VIOLENT, const std::u16string& deathType = u"", uint32_t skillID = 0);
|
||||
|
||||
/**
|
||||
* Pushes a layer of immunity to this entity, making it immune for longer
|
||||
|
@@ -1,4 +1,4 @@
|
||||
#include "InventoryComponent.h"
|
||||
#include "InventoryComponent.h"
|
||||
|
||||
#include <sstream>
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
#include "CharacterComponent.h"
|
||||
#include "dZoneManager.h"
|
||||
#include "PropertyManagementComponent.h"
|
||||
#include "DestroyableComponent.h"
|
||||
|
||||
InventoryComponent::InventoryComponent(Entity* parent, tinyxml2::XMLDocument* document) : Component(parent)
|
||||
{
|
||||
@@ -83,10 +84,13 @@ Inventory* InventoryComponent::GetInventory(const eInventoryType type)
|
||||
case eInventoryType::ITEMS:
|
||||
size = 20u;
|
||||
break;
|
||||
case eInventoryType::VAULT_MODELS:
|
||||
case eInventoryType::VAULT_ITEMS:
|
||||
size = 40u;
|
||||
break;
|
||||
|
||||
case eInventoryType::VENDOR_BUYBACK:
|
||||
size = 27u;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -137,6 +141,7 @@ const EquipmentMap& InventoryComponent::GetEquippedItems() const
|
||||
void InventoryComponent::AddItem(
|
||||
const LOT lot,
|
||||
const uint32_t count,
|
||||
eLootSourceType lootSourceType,
|
||||
eInventoryType inventoryType,
|
||||
const std::vector<LDFBaseData*>& config,
|
||||
const LWOOBJID parent,
|
||||
@@ -176,7 +181,7 @@ void InventoryComponent::AddItem(
|
||||
|
||||
if (!config.empty() || bound)
|
||||
{
|
||||
const auto slot = inventory->FindEmptySlot();
|
||||
const auto slot = preferredSlot != -1 && inventory->IsSlotEmpty(preferredSlot) ? preferredSlot : inventory->FindEmptySlot();
|
||||
|
||||
if (slot == -1)
|
||||
{
|
||||
@@ -185,7 +190,7 @@ void InventoryComponent::AddItem(
|
||||
return;
|
||||
}
|
||||
|
||||
auto* item = new Item(lot, inventory, slot, count, config, parent, showFlyingLoot, isModMoveAndEquip, subKey, bound);
|
||||
auto* item = new Item(lot, inventory, slot, count, config, parent, showFlyingLoot, isModMoveAndEquip, subKey, bound, lootSourceType);
|
||||
|
||||
if (missions != nullptr && !IsTransferInventory(inventoryType))
|
||||
{
|
||||
@@ -203,7 +208,7 @@ void InventoryComponent::AddItem(
|
||||
|
||||
auto stack = static_cast<uint32_t>(info.stackSize);
|
||||
|
||||
if (inventoryType == BRICKS)
|
||||
if (inventoryType == eInventoryType::BRICKS)
|
||||
{
|
||||
stack = 999;
|
||||
}
|
||||
@@ -220,7 +225,7 @@ void InventoryComponent::AddItem(
|
||||
|
||||
left -= delta;
|
||||
|
||||
existing->SetCount(existing->GetCount() + delta, false, true, showFlyingLoot);
|
||||
existing->SetCount(existing->GetCount() + delta, false, true, showFlyingLoot, lootSourceType);
|
||||
|
||||
if (isModMoveAndEquip)
|
||||
{
|
||||
@@ -280,8 +285,7 @@ void InventoryComponent::AddItem(
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
auto* item = new Item(lot, inventory, slot, size, {}, parent, showFlyingLoot, isModMoveAndEquip, subKey);
|
||||
auto* item = new Item(lot, inventory, slot, size, {}, parent, showFlyingLoot, isModMoveAndEquip, subKey, false, lootSourceType);
|
||||
|
||||
isModMoveAndEquip = false;
|
||||
}
|
||||
@@ -365,7 +369,7 @@ void InventoryComponent::MoveItemToInventory(Item* item, const eInventoryType in
|
||||
|
||||
left -= delta;
|
||||
|
||||
AddItem(lot, delta, inventory, {}, LWOOBJID_EMPTY, showFlyingLot, isModMoveAndEquip, LWOOBJID_EMPTY, origin->GetType(), 0, false, preferredSlot);
|
||||
AddItem(lot, delta, eLootSourceType::LOOT_SOURCE_NONE, inventory, {}, LWOOBJID_EMPTY, showFlyingLot, isModMoveAndEquip, LWOOBJID_EMPTY, origin->GetType(), 0, false, preferredSlot);
|
||||
|
||||
item->SetCount(item->GetCount() - delta, false, false);
|
||||
|
||||
@@ -383,7 +387,7 @@ void InventoryComponent::MoveItemToInventory(Item* item, const eInventoryType in
|
||||
|
||||
const auto delta = std::min<uint32_t>(item->GetCount(), count);
|
||||
|
||||
AddItem(lot, delta, inventory, config, LWOOBJID_EMPTY, showFlyingLot, isModMoveAndEquip, LWOOBJID_EMPTY, origin->GetType(), 0, item->GetBound(), preferredSlot);
|
||||
AddItem(lot, delta, eLootSourceType::LOOT_SOURCE_NONE, inventory, config, LWOOBJID_EMPTY, showFlyingLot, isModMoveAndEquip, LWOOBJID_EMPTY, origin->GetType(), 0, item->GetBound(), preferredSlot);
|
||||
|
||||
item->SetCount(item->GetCount() - delta, false, false);
|
||||
}
|
||||
@@ -794,9 +798,28 @@ void InventoryComponent::Serialize(RakNet::BitStream* outBitStream, const bool b
|
||||
|
||||
outBitStream->Write0();
|
||||
|
||||
outBitStream->Write0(); //TODO: This is supposed to be true and write the assemblyPartLOTs when they're present.
|
||||
|
||||
outBitStream->Write1();
|
||||
bool flag = !item.config.empty();
|
||||
outBitStream->Write(flag);
|
||||
if (flag) {
|
||||
RakNet::BitStream ldfStream;
|
||||
ldfStream.Write<int32_t>(item.config.size()); // Key count
|
||||
for (LDFBaseData* data : item.config) {
|
||||
if (data->GetKey() == u"assemblyPartLOTs") {
|
||||
std::string newRocketStr = data->GetValueAsString() + ";";
|
||||
GeneralUtils::ReplaceInString(newRocketStr, "+", ";");
|
||||
LDFData<std::u16string>* ldf_data = new LDFData<std::u16string>(u"assemblyPartLOTs", GeneralUtils::ASCIIToUTF16(newRocketStr));
|
||||
ldf_data->WriteToPacket(&ldfStream);
|
||||
delete ldf_data;
|
||||
} else {
|
||||
data->WriteToPacket(&ldfStream);
|
||||
}
|
||||
}
|
||||
outBitStream->Write(ldfStream.GetNumberOfBytesUsed() + 1);
|
||||
outBitStream->Write<uint8_t>(0); // Don't compress
|
||||
outBitStream->Write(ldfStream);
|
||||
}
|
||||
|
||||
outBitStream->Write1();
|
||||
}
|
||||
|
||||
m_Dirty = false;
|
||||
@@ -908,28 +931,46 @@ void InventoryComponent::EquipItem(Item* item, const bool skipChecks)
|
||||
const auto building = character->GetBuildMode();
|
||||
|
||||
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);
|
||||
|
||||
if (item->GetLot() == 8092 && m_Parent->GetGMLevel() >= GAME_MASTER_LEVEL_DEVELOPER)
|
||||
{
|
||||
EntityInfo info {};
|
||||
info.lot = 8092;
|
||||
info.pos = m_Parent->GetPosition();
|
||||
info.rot = m_Parent->GetRotation();
|
||||
info.pos = startPosition;
|
||||
info.rot = startRotation;
|
||||
info.spawnerID = m_Parent->GetObjectID();
|
||||
|
||||
auto* carEntity = EntityManager::Instance()->CreateEntity(info, nullptr, dZoneManager::Instance()->GetZoneControlObject());
|
||||
dZoneManager::Instance()->GetZoneControlObject()->AddChild(carEntity);
|
||||
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)
|
||||
if (moduleAssemblyComponent != nullptr)
|
||||
{
|
||||
moduleAssemblyComponent->SetSubKey(item->GetSubKey());
|
||||
moduleAssemblyComponent->SetUseOptionalParts(false);
|
||||
@@ -942,11 +983,12 @@ void InventoryComponent::EquipItem(Item* item, const bool skipChecks)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// #107
|
||||
auto* possessorComponent = m_Parent->GetComponent<PossessorComponent>();
|
||||
|
||||
if (possessorComponent != nullptr)
|
||||
{
|
||||
previousPossessorID = possessorComponent->GetPossessable();
|
||||
possessorComponent->SetPossessable(carEntity->GetObjectID());
|
||||
}
|
||||
|
||||
@@ -960,13 +1002,26 @@ void InventoryComponent::EquipItem(Item* item, const bool skipChecks)
|
||||
|
||||
EntityManager::Instance()->ConstructEntity(carEntity);
|
||||
EntityManager::Instance()->SerializeEntity(m_Parent);
|
||||
//EntityManager::Instance()->SerializeEntity(dZoneManager::Instance()->GetZoneControlObject());
|
||||
GameMessages::SendSetJetPackMode(m_Parent, false);
|
||||
|
||||
GameMessages::SendNotifyVehicleOfRacingObject(carEntity->GetObjectID(), dZoneManager::Instance()->GetZoneControlObject()->GetObjectID(), UNASSIGNED_SYSTEM_ADDRESS);
|
||||
GameMessages::SendRacingPlayerLoaded(m_Parent->GetObjectID(), m_Parent->GetObjectID(), carEntity->GetObjectID(), UNASSIGNED_SYSTEM_ADDRESS);
|
||||
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::SendVehicleSetWheelLockState(carEntity->GetObjectID(), false, 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;
|
||||
}
|
||||
|
||||
@@ -1007,11 +1062,11 @@ void InventoryComponent::EquipItem(Item* item, const bool skipChecks)
|
||||
}
|
||||
|
||||
GenerateProxies(item);
|
||||
|
||||
UpdateSlot(item->GetInfo().equipLocation, { item->GetId(), item->GetLot(), item->GetCount(), item->GetSlot(), item->GetConfig() });
|
||||
|
||||
UpdateSlot(item->GetInfo().equipLocation, { item->GetId(), item->GetLot(), item->GetCount(), item->GetSlot() });
|
||||
|
||||
ApplyBuff(item->GetLot());
|
||||
|
||||
ApplyBuff(item);
|
||||
|
||||
AddItemSkills(item->GetLot());
|
||||
|
||||
EntityManager::Instance()->SerializeEntity(m_Parent);
|
||||
@@ -1038,8 +1093,8 @@ void InventoryComponent::UnEquipItem(Item* item)
|
||||
set->OnUnEquip(lot);
|
||||
}
|
||||
|
||||
RemoveBuff(item->GetLot());
|
||||
|
||||
RemoveBuff(item);
|
||||
|
||||
RemoveItemSkills(item->GetLot());
|
||||
|
||||
RemoveSlot(item->GetInfo().equipLocation);
|
||||
@@ -1056,9 +1111,9 @@ void InventoryComponent::UnEquipItem(Item* item)
|
||||
}
|
||||
}
|
||||
|
||||
void InventoryComponent::ApplyBuff(const LOT lot) const
|
||||
void InventoryComponent::ApplyBuff(Item* item) const
|
||||
{
|
||||
const auto buffs = FindBuffs(lot, true);
|
||||
const auto buffs = FindBuffs(item, true);
|
||||
|
||||
for (const auto buff : buffs)
|
||||
{
|
||||
@@ -1066,9 +1121,9 @@ void InventoryComponent::ApplyBuff(const LOT lot) const
|
||||
}
|
||||
}
|
||||
|
||||
void InventoryComponent::RemoveBuff(const LOT lot) const
|
||||
void InventoryComponent::RemoveBuff(Item* item) const
|
||||
{
|
||||
const auto buffs = FindBuffs(lot, false);
|
||||
const auto buffs = FindBuffs(item, false);
|
||||
|
||||
for (const auto buff : buffs)
|
||||
{
|
||||
@@ -1195,13 +1250,6 @@ void InventoryComponent::AddItemSkills(const LOT lot)
|
||||
|
||||
const auto index = m_Skills.find(slot);
|
||||
|
||||
if (index != m_Skills.end())
|
||||
{
|
||||
const auto old = index->second;
|
||||
|
||||
GameMessages::SendRemoveSkill(m_Parent, old);
|
||||
}
|
||||
|
||||
const auto skill = FindSkill(lot);
|
||||
|
||||
if (skill == 0)
|
||||
@@ -1209,6 +1257,13 @@ void InventoryComponent::AddItemSkills(const LOT lot)
|
||||
return;
|
||||
}
|
||||
|
||||
if (index != m_Skills.end())
|
||||
{
|
||||
const auto old = index->second;
|
||||
|
||||
GameMessages::SendRemoveSkill(m_Parent, old);
|
||||
}
|
||||
|
||||
GameMessages::SendAddSkill(m_Parent, skill, static_cast<int>(slot));
|
||||
|
||||
m_Skills.insert_or_assign(slot, skill);
|
||||
@@ -1384,18 +1439,18 @@ uint32_t InventoryComponent::FindSkill(const LOT lot)
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::vector<uint32_t> InventoryComponent::FindBuffs(const LOT lot, bool castOnEquip) const
|
||||
std::vector<uint32_t> InventoryComponent::FindBuffs(Item* item, bool castOnEquip) const
|
||||
{
|
||||
std::vector<uint32_t> buffs;
|
||||
if (item == nullptr) return buffs;
|
||||
auto* table = CDClientManager::Instance()->GetTable<CDObjectSkillsTable>("ObjectSkills");
|
||||
auto* behaviors = CDClientManager::Instance()->GetTable<CDSkillBehaviorTable>("SkillBehavior");
|
||||
|
||||
const auto results = table->Query([=](const CDObjectSkills& entry)
|
||||
{
|
||||
return entry.objectTemplate == static_cast<unsigned int>(lot);
|
||||
return entry.objectTemplate == static_cast<unsigned int>(item->GetLot());
|
||||
});
|
||||
|
||||
std::vector<uint32_t> buffs;
|
||||
|
||||
auto* missions = static_cast<MissionComponent*>(m_Parent->GetComponent(COMPONENT_TYPE_MISSION));
|
||||
|
||||
for (const auto& result : results)
|
||||
@@ -1415,8 +1470,9 @@ std::vector<uint32_t> InventoryComponent::FindBuffs(const LOT lot, bool castOnEq
|
||||
{
|
||||
missions->Progress(MissionTaskType::MISSION_TASK_TYPE_SKILL, result.skillID);
|
||||
}
|
||||
|
||||
buffs.push_back(static_cast<uint32_t>(entry.behaviorID));
|
||||
|
||||
// If item is not a proxy, add its buff to the added buffs.
|
||||
if (item->GetParent() == LWOOBJID_EMPTY) buffs.push_back(static_cast<uint32_t>(entry.behaviorID));
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -15,6 +15,7 @@
|
||||
#include "Component.h"
|
||||
#include "ItemSetPassiveAbility.h"
|
||||
#include "ItemSetPassiveAbilityID.h"
|
||||
#include "PossessorComponent.h"
|
||||
|
||||
class Entity;
|
||||
class ItemSet;
|
||||
@@ -86,10 +87,12 @@ public:
|
||||
* @param sourceType the source of the item, used to determine if the item is dropped or mailed if the inventory is full
|
||||
* @param bound whether this item is bound
|
||||
* @param preferredSlot the preferred slot to store this item
|
||||
* @param lootSourceType The source of the loot. Defaults to none.
|
||||
*/
|
||||
void AddItem(
|
||||
LOT lot,
|
||||
uint32_t count,
|
||||
eLootSourceType lootSourceType = eLootSourceType::LOOT_SOURCE_NONE,
|
||||
eInventoryType inventoryType = INVALID,
|
||||
const std::vector<LDFBaseData*>& config = {},
|
||||
LWOOBJID parent = LWOOBJID_EMPTY,
|
||||
@@ -192,15 +195,15 @@ public:
|
||||
|
||||
/**
|
||||
* Adds a buff related to equipping a lot to the entity
|
||||
* @param lot the lot to find buffs for
|
||||
* @param item the item to find buffs for
|
||||
*/
|
||||
void ApplyBuff(LOT lot) const;
|
||||
void ApplyBuff(Item* item) const;
|
||||
|
||||
/**
|
||||
* Removes buffs related to equipping a lot from the entity
|
||||
* @param lot the lot to find buffs for
|
||||
* @param item the item to find buffs for
|
||||
*/
|
||||
void RemoveBuff(LOT lot) const;
|
||||
void RemoveBuff(Item* item) const;
|
||||
|
||||
/**
|
||||
* Saves the equipped items into a temp state
|
||||
@@ -239,11 +242,11 @@ public:
|
||||
|
||||
/**
|
||||
* Finds all the buffs related to a lot
|
||||
* @param lot the lot to get the buffs for
|
||||
* @param item the item to get the buffs for
|
||||
* @param castOnEquip if true, the skill missions for these buffs will be progressed
|
||||
* @return the buffs related to the specified lot
|
||||
*/
|
||||
std::vector<uint32_t> FindBuffs(LOT lot, bool castOnEquip) const;
|
||||
std::vector<uint32_t> FindBuffs(Item* item, bool castOnEquip) const;
|
||||
|
||||
/**
|
||||
* Initializes the equipped items with a list of items
|
||||
@@ -384,6 +387,13 @@ private:
|
||||
*/
|
||||
LOT m_Consumable;
|
||||
|
||||
/**
|
||||
* Currently has a car equipped
|
||||
*/
|
||||
bool hasCarEquipped = false;
|
||||
Entity* equippedCarEntity = nullptr;
|
||||
LWOOBJID previousPossessableID = LWOOBJID_EMPTY;
|
||||
LWOOBJID previousPossessorID = LWOOBJID_EMPTY;
|
||||
/**
|
||||
* Creates all the proxy items (subitems) for a parent item
|
||||
* @param parent the parent item to generate all the subitems for
|
||||
@@ -399,8 +409,8 @@ private:
|
||||
std::vector<Item*> FindProxies(LWOOBJID parent);
|
||||
|
||||
/**
|
||||
* Returns if the provided ID is a valid proxy item (e.g. we have children for it)
|
||||
* @param parent the parent item to check for
|
||||
* Returns true if the provided LWOOBJID is the parent of this Item.
|
||||
* @param parent the parent item to check for proxies
|
||||
* @return if the provided ID is a valid proxy item
|
||||
*/
|
||||
bool IsValidProxy(LWOOBJID parent);
|
||||
|
@@ -29,8 +29,8 @@ public:
|
||||
explicit MissionComponent(Entity* parent);
|
||||
~MissionComponent() override;
|
||||
void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags);
|
||||
void LoadFromXml(tinyxml2::XMLDocument* doc);
|
||||
void UpdateXml(tinyxml2::XMLDocument* doc);
|
||||
void LoadFromXml(tinyxml2::XMLDocument* doc) override;
|
||||
void UpdateXml(tinyxml2::XMLDocument* doc) override;
|
||||
|
||||
/**
|
||||
* Returns all the missions for this entity, mapped by mission ID
|
||||
|
@@ -17,7 +17,7 @@ public:
|
||||
~ModuleAssemblyComponent() override;
|
||||
|
||||
void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags);
|
||||
void Update(float deltaTime);
|
||||
void Update(float deltaTime) override;
|
||||
|
||||
/**
|
||||
* Sets the subkey of this entity
|
||||
|
@@ -61,7 +61,7 @@ public:
|
||||
MovementAIComponent(Entity* parentEntity, MovementAIInfo info);
|
||||
~MovementAIComponent() override;
|
||||
|
||||
void Update(float deltaTime);
|
||||
void Update(float deltaTime) override;
|
||||
|
||||
/**
|
||||
* Returns the basic settings that this entity uses to move around
|
||||
|
@@ -75,13 +75,19 @@ PetComponent::PetComponent(Entity* parent, uint32_t componentId) : Component(par
|
||||
m_MovementAI = nullptr;
|
||||
m_TresureTime = 0;
|
||||
m_Preconditions = nullptr;
|
||||
|
||||
std::string checkPreconditions = GeneralUtils::UTF16ToWTF8(parent->GetVar<std::u16string>(u"CheckPrecondition"));
|
||||
|
||||
if (!checkPreconditions.empty()) {
|
||||
SetPreconditions(checkPreconditions);
|
||||
}
|
||||
}
|
||||
|
||||
void PetComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags)
|
||||
{
|
||||
const bool tamed = m_Owner != LWOOBJID_EMPTY;
|
||||
|
||||
outBitStream->Write1(); // Dirty?
|
||||
outBitStream->Write1(); // Always serialize as dirty for now
|
||||
|
||||
outBitStream->Write<uint32_t>(static_cast<unsigned int>(m_Status));
|
||||
outBitStream->Write<uint32_t>(static_cast<uint32_t>(tamed ? m_Ability : PetAbilityType::Invalid)); // Something with the overhead icon?
|
||||
@@ -262,29 +268,12 @@ void PetComponent::OnUse(Entity* originator)
|
||||
|
||||
auto position = originatorPosition;
|
||||
|
||||
NiPoint3 forward = NiQuaternion::LookAt(m_Parent->GetPosition(), originator->GetPosition()).GetForwardVector(); //m_Parent->GetRotation().GetForwardVector();
|
||||
NiPoint3 forward = NiQuaternion::LookAt(m_Parent->GetPosition(), originator->GetPosition()).GetForwardVector();
|
||||
forward.y = 0;
|
||||
|
||||
if (dpWorld::Instance().IsLoaded())
|
||||
{
|
||||
/*
|
||||
if (interactionDistance > 2)
|
||||
{
|
||||
interactionDistance -= 1;
|
||||
}
|
||||
*/
|
||||
|
||||
NiPoint3 attempt = petPosition + forward * interactionDistance;
|
||||
|
||||
/*
|
||||
float deg = std::atan2(petPosition.z - originatorPosition.z, petPosition.x - originatorPosition.x); //* 180 / M_PI;
|
||||
|
||||
auto position = NiPoint3(
|
||||
petPosition.x + interactionDistance * std::cos(-deg),
|
||||
petPosition.y,
|
||||
petPosition.z + interactionDistance * std::sin(-deg)
|
||||
);
|
||||
*/
|
||||
|
||||
float y = dpWorld::Instance().GetHeightAtPoint(attempt);
|
||||
|
||||
@@ -308,8 +297,6 @@ void PetComponent::OnUse(Entity* originator)
|
||||
|
||||
|
||||
auto rotation = NiQuaternion::LookAt(position, petPosition);
|
||||
|
||||
//GameMessages::SendTeleport(originator->GetObjectID(), position, rotation, originator->GetSystemAddress(), true);
|
||||
|
||||
GameMessages::SendNotifyPetTamingMinigame(
|
||||
originator->GetObjectID(),
|
||||
@@ -531,14 +518,12 @@ void PetComponent::Update(float deltaTime)
|
||||
m_Timer = 1;
|
||||
}
|
||||
|
||||
void PetComponent::TryBuild(std::vector<Brick>& bricks, bool clientFailed)
|
||||
{
|
||||
void PetComponent::TryBuild(uint32_t numBricks, bool clientFailed) {
|
||||
if (m_Tamer == LWOOBJID_EMPTY) return;
|
||||
|
||||
auto* tamer = EntityManager::Instance()->GetEntity(m_Tamer);
|
||||
|
||||
if (tamer == nullptr)
|
||||
{
|
||||
if (tamer == nullptr) {
|
||||
m_Tamer = LWOOBJID_EMPTY;
|
||||
|
||||
return;
|
||||
@@ -546,19 +531,11 @@ void PetComponent::TryBuild(std::vector<Brick>& bricks, bool clientFailed)
|
||||
|
||||
const auto& cached = buildCache.find(m_Parent->GetLOT());
|
||||
|
||||
if (cached == buildCache.end())
|
||||
{
|
||||
GameMessages::SendPetTamingTryBuildResult(m_Tamer, false, 0, tamer->GetSystemAddress());
|
||||
|
||||
return;
|
||||
}
|
||||
if (cached == buildCache.end()) return;
|
||||
|
||||
auto* destroyableComponent = tamer->GetComponent<DestroyableComponent>();
|
||||
|
||||
if (destroyableComponent == nullptr)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (destroyableComponent == nullptr) return;
|
||||
|
||||
auto imagination = destroyableComponent->GetImagination();
|
||||
|
||||
@@ -568,59 +545,17 @@ void PetComponent::TryBuild(std::vector<Brick>& bricks, bool clientFailed)
|
||||
|
||||
EntityManager::Instance()->SerializeEntity(tamer);
|
||||
|
||||
const auto& trueBricks = BrickDatabase::Instance()->GetBricks(cached->second.buildFile);
|
||||
|
||||
if (trueBricks.empty() || bricks.empty())
|
||||
{
|
||||
GameMessages::SendPetTamingTryBuildResult(m_Tamer, false, 0, tamer->GetSystemAddress());
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
auto* brickIDTable = CDClientManager::Instance()->GetTable<CDBrickIDTableTable>("BrickIDTable");
|
||||
|
||||
int32_t correct = 0;
|
||||
|
||||
for (const auto& brick : bricks)
|
||||
{
|
||||
const auto brickEntries = brickIDTable->Query([brick](const CDBrickIDTable& entry)
|
||||
{
|
||||
return entry.NDObjectID == brick.designerID;
|
||||
});
|
||||
|
||||
if (brickEntries.empty())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto designerID = brickEntries[0].LEGOBrickID;
|
||||
|
||||
for (const auto& trueBrick : trueBricks)
|
||||
{
|
||||
if (designerID == trueBrick.designerID && brick.materialID == trueBrick.materialID)
|
||||
{
|
||||
correct++;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const auto success = correct >= cached->second.numValidPieces;
|
||||
|
||||
GameMessages::SendPetTamingTryBuildResult(m_Tamer, success, correct, tamer->GetSystemAddress());
|
||||
|
||||
if (!success)
|
||||
{
|
||||
if (imagination < cached->second.imaginationCost)
|
||||
{
|
||||
if (clientFailed) {
|
||||
if (imagination < cached->second.imaginationCost) {
|
||||
ClientFailTamingMinigame();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
m_Timer = 0;
|
||||
}
|
||||
|
||||
if (numBricks == 0) return;
|
||||
|
||||
GameMessages::SendPetTamingTryBuildResult(m_Tamer, !clientFailed, numBricks, tamer->GetSystemAddress());
|
||||
}
|
||||
|
||||
void PetComponent::NotifyTamingBuildSuccess(NiPoint3 position)
|
||||
@@ -685,7 +620,7 @@ void PetComponent::NotifyTamingBuildSuccess(NiPoint3 position)
|
||||
|
||||
GameMessages::SendRegisterPetDBID(m_Tamer, petSubKey, tamer->GetSystemAddress());
|
||||
|
||||
inventoryComponent->AddItem(m_Parent->GetLOT(), 1, MODELS, {}, LWOOBJID_EMPTY, true, false, petSubKey);
|
||||
inventoryComponent->AddItem(m_Parent->GetLOT(), 1, eLootSourceType::LOOT_SOURCE_ACTIVITY, eInventoryType::MODELS, {}, LWOOBJID_EMPTY, true, false, petSubKey);
|
||||
auto* item = inventoryComponent->FindItemBySubKey(petSubKey, MODELS);
|
||||
|
||||
if (item == nullptr)
|
||||
@@ -726,7 +661,6 @@ void PetComponent::NotifyTamingBuildSuccess(NiPoint3 position)
|
||||
|
||||
if (missionComponent != nullptr)
|
||||
{
|
||||
//missionComponent->ForceProgress(506, 768, 1, false);
|
||||
missionComponent->Progress(MissionTaskType::MISSION_TASK_TYPE_PET_TAMING, m_Parent->GetLOT());
|
||||
}
|
||||
|
||||
|
@@ -39,7 +39,7 @@ public:
|
||||
* @param bricks the bricks to try to complete the minigame with
|
||||
* @param clientFailed unused
|
||||
*/
|
||||
void TryBuild(std::vector<Brick>& bricks, bool clientFailed);
|
||||
void TryBuild(uint32_t numBricks, bool clientFailed);
|
||||
|
||||
/**
|
||||
* Handles a notification from the client regarding the completion of the pet minigame, adding the pet to their
|
||||
|
@@ -1,12 +1,16 @@
|
||||
#include <CDPropertyEntranceComponentTable.h>
|
||||
#include "PropertyEntranceComponent.h"
|
||||
|
||||
#include <CDPropertyEntranceComponentTable.h>
|
||||
|
||||
#include "Character.h"
|
||||
#include "Database.h"
|
||||
#include "GameMessages.h"
|
||||
#include "PropertyManagementComponent.h"
|
||||
#include "PropertySelectQueryProperty.h"
|
||||
#include "RocketLaunchpadControlComponent.h"
|
||||
#include "Character.h"
|
||||
#include "GameMessages.h"
|
||||
#include "CharacterComponent.h"
|
||||
#include "UserManager.h"
|
||||
#include "dLogger.h"
|
||||
#include "Database.h"
|
||||
#include "PropertyManagementComponent.h"
|
||||
|
||||
PropertyEntranceComponent::PropertyEntranceComponent(uint32_t componentID, Entity* parent) : Component(parent)
|
||||
{
|
||||
@@ -19,20 +23,25 @@ PropertyEntranceComponent::PropertyEntranceComponent(uint32_t componentID, Entit
|
||||
this->m_PropertyName = entry.propertyName;
|
||||
}
|
||||
|
||||
void PropertyEntranceComponent::OnUse(Entity* entity)
|
||||
{
|
||||
GameMessages::SendPropertyEntranceBegin(m_Parent->GetObjectID(), entity->GetSystemAddress());
|
||||
void PropertyEntranceComponent::OnUse(Entity* entity) {
|
||||
auto* characterComponent = entity->GetComponent<CharacterComponent>();
|
||||
if (!characterComponent) return;
|
||||
|
||||
AMFArrayValue args;
|
||||
auto* rocket = entity->GetComponent<CharacterComponent>()->RocketEquip(entity);
|
||||
if (!rocket) return;
|
||||
|
||||
GameMessages::SendPropertyEntranceBegin(m_Parent->GetObjectID(), entity->GetSystemAddress());
|
||||
|
||||
AMFArrayValue args;
|
||||
|
||||
auto* state = new AMFStringValue();
|
||||
state->SetStringValue("property_menu");
|
||||
|
||||
args.InsertValue("state", state);
|
||||
|
||||
GameMessages::SendUIMessageServerToSingleClient(entity, entity->GetSystemAddress(), "pushGameState", &args);
|
||||
GameMessages::SendUIMessageServerToSingleClient(entity, entity->GetSystemAddress(), "pushGameState", &args);
|
||||
|
||||
delete state;
|
||||
delete state;
|
||||
}
|
||||
|
||||
void PropertyEntranceComponent::OnEnterProperty(Entity* entity, uint32_t index, bool returnToZone, const SystemAddress& sysAddr)
|
||||
@@ -49,6 +58,7 @@ void PropertyEntranceComponent::OnEnterProperty(Entity* entity, uint32_t index,
|
||||
}
|
||||
else if (index >= 0)
|
||||
{
|
||||
// Increment index once here because the first index of other player properties is 2 in the propertyQueries cache.
|
||||
index++;
|
||||
|
||||
const auto& pair = propertyQueries.find(entity->GetObjectID());
|
||||
@@ -71,167 +81,269 @@ void PropertyEntranceComponent::OnEnterProperty(Entity* entity, uint32_t index,
|
||||
|
||||
launcher->SetSelectedCloneId(entity->GetObjectID(), cloneId);
|
||||
|
||||
launcher->Launch(entity, LWOOBJID_EMPTY, LWOMAPID_INVALID, cloneId);
|
||||
launcher->Launch(entity, launcher->GetTargetZone(), cloneId);
|
||||
}
|
||||
|
||||
void PropertyEntranceComponent::OnPropertyEntranceSync(Entity* entity,
|
||||
bool includeNullAddress,
|
||||
bool includeNullDescription,
|
||||
bool playerOwn,
|
||||
bool updateUi,
|
||||
int32_t numResults,
|
||||
int32_t reputation,
|
||||
int32_t sortMethod,
|
||||
int32_t startIndex,
|
||||
std::string filterText,
|
||||
const SystemAddress& sysAddr)
|
||||
{
|
||||
Game::logger->Log("PropertyEntranceComponent", "On Sync %d %d %d %d %i %i %i %i %s\n",
|
||||
includeNullAddress,
|
||||
includeNullDescription,
|
||||
playerOwn,
|
||||
updateUi,
|
||||
numResults,
|
||||
reputation,
|
||||
sortMethod,
|
||||
startIndex,
|
||||
filterText.c_str()
|
||||
);
|
||||
PropertySelectQueryProperty PropertyEntranceComponent::SetPropertyValues(PropertySelectQueryProperty property, LWOCLONEID cloneId, std::string ownerName, std::string propertyName, std::string propertyDescription, float reputation, bool isBFF, bool isFriend, bool isModeratorApproved, bool isAlt, bool isOwned, uint32_t privacyOption, uint32_t timeLastUpdated, float performanceCost) {
|
||||
property.CloneId = cloneId;
|
||||
property.OwnerName = ownerName;
|
||||
property.Name = propertyName;
|
||||
property.Description = propertyDescription;
|
||||
property.Reputation = reputation;
|
||||
property.IsBestFriend = isBFF;
|
||||
property.IsFriend = isFriend;
|
||||
property.IsModeratorApproved = isModeratorApproved;
|
||||
property.IsAlt = isAlt;
|
||||
property.IsOwned = isOwned;
|
||||
property.AccessType = privacyOption;
|
||||
property.DateLastPublished = timeLastUpdated;
|
||||
property.PerformanceCost = performanceCost;
|
||||
|
||||
auto* launchpadComponent = m_Parent->GetComponent<RocketLaunchpadControlComponent>();
|
||||
if (launchpadComponent == nullptr)
|
||||
return;
|
||||
return property;
|
||||
}
|
||||
|
||||
std::string PropertyEntranceComponent::BuildQuery(Entity* entity, int32_t sortMethod, std::string customQuery, bool wantLimits) {
|
||||
std::string base;
|
||||
if (customQuery == "") {
|
||||
base = baseQueryForProperties;
|
||||
} else {
|
||||
base = customQuery;
|
||||
}
|
||||
std::string orderBy = "";
|
||||
if (sortMethod == SORT_TYPE_FEATURED || sortMethod == SORT_TYPE_FRIENDS) {
|
||||
std::string friendsList = " AND p.owner_id IN (";
|
||||
|
||||
auto friendsListQuery = Database::CreatePreppedStmt("SELECT * FROM (SELECT CASE WHEN player_id = ? THEN friend_id WHEN friend_id = ? THEN player_id END AS requested_player FROM friends ) AS fr WHERE requested_player IS NOT NULL ORDER BY requested_player DESC;");
|
||||
|
||||
friendsListQuery->setInt64(1, entity->GetObjectID());
|
||||
friendsListQuery->setInt64(2, entity->GetObjectID());
|
||||
|
||||
auto friendsListQueryResult = friendsListQuery->executeQuery();
|
||||
|
||||
while (friendsListQueryResult->next()) {
|
||||
auto playerIDToConvert = friendsListQueryResult->getInt64(1);
|
||||
playerIDToConvert = GeneralUtils::ClearBit(playerIDToConvert, OBJECT_BIT_CHARACTER);
|
||||
playerIDToConvert = GeneralUtils::ClearBit(playerIDToConvert, OBJECT_BIT_PERSISTENT);
|
||||
friendsList = friendsList + std::to_string(playerIDToConvert) + ",";
|
||||
}
|
||||
// Replace trailing comma with the closing parenthesis.
|
||||
if (friendsList.at(friendsList.size() - 1) == ',') friendsList.erase(friendsList.size() - 1, 1);
|
||||
friendsList += ") ";
|
||||
|
||||
// If we have no friends then use a -1 for the query.
|
||||
if (friendsList.find("()") != std::string::npos) friendsList = " AND p.owner_id IN (-1) ";
|
||||
|
||||
orderBy += friendsList + "ORDER BY ci.name ASC ";
|
||||
|
||||
delete friendsListQueryResult;
|
||||
friendsListQueryResult = nullptr;
|
||||
|
||||
delete friendsListQuery;
|
||||
friendsListQuery = nullptr;
|
||||
}
|
||||
else if (sortMethod == SORT_TYPE_RECENT) {
|
||||
orderBy = "ORDER BY p.last_updated DESC ";
|
||||
}
|
||||
else if (sortMethod == SORT_TYPE_REPUTATION) {
|
||||
orderBy = "ORDER BY p.reputation DESC, p.last_updated DESC ";
|
||||
}
|
||||
else {
|
||||
orderBy = "ORDER BY p.last_updated DESC ";
|
||||
}
|
||||
return base + orderBy + (wantLimits ? "LIMIT ? OFFSET ?;" : ";");
|
||||
}
|
||||
|
||||
void PropertyEntranceComponent::OnPropertyEntranceSync(Entity* entity, bool includeNullAddress, bool includeNullDescription, bool playerOwn, bool updateUi, int32_t numResults, int32_t lReputationTime, int32_t sortMethod, int32_t startIndex, std::string filterText, const SystemAddress& sysAddr){
|
||||
|
||||
std::vector<PropertySelectQueryProperty> entries {};
|
||||
PropertySelectQueryProperty playerEntry {};
|
||||
|
||||
auto* character = entity->GetCharacter();
|
||||
playerEntry.OwnerName = character->GetName();
|
||||
playerEntry.Description = "No description.";
|
||||
playerEntry.Name = "Your property!";
|
||||
playerEntry.IsModeratorApproved = true;
|
||||
playerEntry.AccessType = 2;
|
||||
playerEntry.CloneId = character->GetPropertyCloneID();
|
||||
auto character = entity->GetCharacter();
|
||||
if (!character) return;
|
||||
|
||||
// Player property goes in index 1 of the vector. This is how the client expects it.
|
||||
auto playerPropertyLookup = Database::CreatePreppedStmt("SELECT * FROM properties WHERE owner_id = ? AND zone_id = ?");
|
||||
|
||||
playerPropertyLookup->setInt(1, character->GetID());
|
||||
playerPropertyLookup->setInt(2, this->m_MapID);
|
||||
|
||||
auto playerPropertyLookupResults = playerPropertyLookup->executeQuery();
|
||||
|
||||
// If the player has a property this query will have a single result.
|
||||
if (playerPropertyLookupResults->next()) {
|
||||
const auto cloneId = playerPropertyLookupResults->getUInt64(4);
|
||||
const auto propertyName = playerPropertyLookupResults->getString(5).asStdString();
|
||||
const auto propertyDescription = playerPropertyLookupResults->getString(6).asStdString();
|
||||
const auto privacyOption = playerPropertyLookupResults->getInt(9);
|
||||
const auto modApproved = playerPropertyLookupResults->getBoolean(10);
|
||||
const auto dateLastUpdated = playerPropertyLookupResults->getInt64(11);
|
||||
const auto reputation = playerPropertyLookupResults->getUInt(14);
|
||||
const auto performanceCost = (float)playerPropertyLookupResults->getDouble(16);
|
||||
|
||||
playerEntry = SetPropertyValues(playerEntry, cloneId, character->GetName(), propertyName, propertyDescription, reputation, true, true, modApproved, true, true, privacyOption, dateLastUpdated, performanceCost);
|
||||
} else {
|
||||
playerEntry = SetPropertyValues(playerEntry, character->GetPropertyCloneID(), character->GetName(), "", "", 0, true, true);
|
||||
}
|
||||
|
||||
delete playerPropertyLookupResults;
|
||||
playerPropertyLookupResults = nullptr;
|
||||
|
||||
delete playerPropertyLookup;
|
||||
playerPropertyLookup = nullptr;
|
||||
|
||||
entries.push_back(playerEntry);
|
||||
|
||||
sql::ResultSet* propertyEntry;
|
||||
sql::PreparedStatement* propertyLookup;
|
||||
const auto query = BuildQuery(entity, sortMethod);
|
||||
|
||||
const auto moderating = entity->GetGMLevel() >= GAME_MASTER_LEVEL_LEAD_MODERATOR;
|
||||
|
||||
if (!moderating)
|
||||
{
|
||||
propertyLookup = Database::CreatePreppedStmt(
|
||||
"SELECT * FROM properties WHERE (name LIKE ? OR description LIKE ? OR "
|
||||
"((SELECT name FROM charinfo WHERE prop_clone_id = clone_id) LIKE ?)) AND "
|
||||
"(privacy_option = 2 AND mod_approved = true) OR (privacy_option >= 1 "
|
||||
"AND (owner_id IN (SELECT friend_id FROM friends WHERE player_id = ?) OR owner_id IN (SELECT player_id FROM "
|
||||
"friends WHERE friend_id = ?))) AND zone_id = ? LIMIT ? OFFSET ?;"
|
||||
);
|
||||
auto propertyLookup = Database::CreatePreppedStmt(query);
|
||||
|
||||
const auto searchString = "%" + filterText + "%";
|
||||
propertyLookup->setUInt(1, this->m_MapID);
|
||||
propertyLookup->setString(2, searchString.c_str());
|
||||
propertyLookup->setString(3, searchString.c_str());
|
||||
propertyLookup->setString(4, searchString.c_str());
|
||||
propertyLookup->setInt(5, sortMethod == SORT_TYPE_FEATURED || sortMethod == SORT_TYPE_FRIENDS ? (uint32_t)PropertyPrivacyOption::Friends : (uint32_t)PropertyPrivacyOption::Public);
|
||||
propertyLookup->setInt(6, numResults);
|
||||
propertyLookup->setInt(7, startIndex);
|
||||
|
||||
const std::string searchString = "%" + filterText + "%";
|
||||
Game::logger->Log("PropertyEntranceComponent", "%s\n", searchString.c_str());
|
||||
propertyLookup->setString(1, searchString.c_str());
|
||||
propertyLookup->setString(2, searchString.c_str());
|
||||
propertyLookup->setString(3, searchString.c_str());
|
||||
propertyLookup->setInt64(4, entity->GetObjectID());
|
||||
propertyLookup->setInt64(5, entity->GetObjectID());
|
||||
propertyLookup->setUInt(6, launchpadComponent->GetTargetZone());
|
||||
propertyLookup->setInt(7, numResults);
|
||||
propertyLookup->setInt(8, startIndex);
|
||||
auto propertyEntry = propertyLookup->executeQuery();
|
||||
|
||||
propertyEntry = propertyLookup->executeQuery();
|
||||
}
|
||||
else
|
||||
{
|
||||
propertyLookup = Database::CreatePreppedStmt(
|
||||
"SELECT * FROM properties WHERE privacy_option = 2 AND mod_approved = false AND zone_id = ?;"
|
||||
);
|
||||
|
||||
propertyLookup->setUInt(1, launchpadComponent->GetTargetZone());
|
||||
|
||||
propertyEntry = propertyLookup->executeQuery();
|
||||
}
|
||||
|
||||
while (propertyEntry->next())
|
||||
{
|
||||
const auto propertyId = propertyEntry->getUInt64(1);
|
||||
const auto owner = propertyEntry->getUInt64(2);
|
||||
while (propertyEntry->next()) {
|
||||
const auto propertyId = propertyEntry->getUInt64(1);
|
||||
const auto owner = propertyEntry->getInt(2);
|
||||
const auto cloneId = propertyEntry->getUInt64(4);
|
||||
const auto name = propertyEntry->getString(5).asStdString();
|
||||
const auto description = propertyEntry->getString(6).asStdString();
|
||||
const auto privacyOption = propertyEntry->getInt(9);
|
||||
const auto reputation = propertyEntry->getInt(15);
|
||||
const auto propertyNameFromDb = propertyEntry->getString(5).asStdString();
|
||||
const auto propertyDescriptionFromDb = propertyEntry->getString(6).asStdString();
|
||||
const auto privacyOption = propertyEntry->getInt(9);
|
||||
const auto modApproved = propertyEntry->getBoolean(10);
|
||||
const auto dateLastUpdated = propertyEntry->getInt(11);
|
||||
const float reputation = propertyEntry->getInt(14);
|
||||
const auto performanceCost = (float)propertyEntry->getDouble(16);
|
||||
|
||||
PropertySelectQueryProperty entry {};
|
||||
|
||||
auto* nameLookup = Database::CreatePreppedStmt("SELECT name FROM charinfo WHERE prop_clone_id = ?;");
|
||||
PropertySelectQueryProperty entry{};
|
||||
|
||||
std::string ownerName = "";
|
||||
bool isOwned = true;
|
||||
auto nameLookup = Database::CreatePreppedStmt("SELECT name FROM charinfo WHERE prop_clone_id = ?;");
|
||||
|
||||
nameLookup->setUInt64(1, cloneId);
|
||||
|
||||
auto* nameResult = nameLookup->executeQuery();
|
||||
auto nameResult = nameLookup->executeQuery();
|
||||
|
||||
if (!nameResult->next())
|
||||
{
|
||||
if (!nameResult->next()) {
|
||||
delete nameLookup;
|
||||
nameLookup = nullptr;
|
||||
|
||||
Game::logger->Log("PropertyEntranceComponent", "Failed to find property owner name for %llu!\n", cloneId);
|
||||
|
||||
continue;
|
||||
} else {
|
||||
isOwned = cloneId == character->GetPropertyCloneID();
|
||||
ownerName = nameResult->getString(1).asStdString();
|
||||
}
|
||||
else
|
||||
{
|
||||
entry.IsOwner = owner == entity->GetObjectID();
|
||||
entry.OwnerName = nameResult->getString(1).asStdString();
|
||||
}
|
||||
|
||||
if (!moderating)
|
||||
{
|
||||
entry.Name = name;
|
||||
entry.Description = description;
|
||||
}
|
||||
else
|
||||
{
|
||||
entry.Name = "[Awaiting approval] " + name;
|
||||
entry.Description = "[Awaiting approval] " + description;
|
||||
}
|
||||
|
||||
entry.IsFriend = privacyOption == static_cast<int32_t>(PropertyPrivacyOption::Friends);
|
||||
entry.Reputation = reputation;
|
||||
entry.CloneId = cloneId;
|
||||
entry.IsModeratorApproved = true;
|
||||
entry.AccessType = 3;
|
||||
|
||||
entries.push_back(entry);
|
||||
delete nameResult;
|
||||
nameResult = nullptr;
|
||||
|
||||
delete nameLookup;
|
||||
}
|
||||
nameLookup = nullptr;
|
||||
|
||||
std::string propertyName = propertyNameFromDb;
|
||||
std::string propertyDescription = propertyDescriptionFromDb;
|
||||
|
||||
bool isBestFriend = false;
|
||||
bool isFriend = false;
|
||||
|
||||
// Convert owner char id to LWOOBJID
|
||||
LWOOBJID ownerObjId = owner;
|
||||
ownerObjId = GeneralUtils::SetBit(ownerObjId, OBJECT_BIT_CHARACTER);
|
||||
ownerObjId = GeneralUtils::SetBit(ownerObjId, OBJECT_BIT_PERSISTENT);
|
||||
|
||||
// Query to get friend and best friend fields
|
||||
auto friendCheck = Database::CreatePreppedStmt("SELECT best_friend FROM friends WHERE (player_id = ? AND friend_id = ?) OR (player_id = ? AND friend_id = ?)");
|
||||
|
||||
friendCheck->setInt64(1, entity->GetObjectID());
|
||||
friendCheck->setInt64(2, ownerObjId);
|
||||
friendCheck->setInt64(3, ownerObjId);
|
||||
friendCheck->setInt64(4, entity->GetObjectID());
|
||||
|
||||
auto friendResult = friendCheck->executeQuery();
|
||||
|
||||
// If we got a result than the two players are friends.
|
||||
if (friendResult->next()) {
|
||||
isFriend = true;
|
||||
if (friendResult->getInt(1) == 2) {
|
||||
isBestFriend = true;
|
||||
}
|
||||
}
|
||||
|
||||
delete friendCheck;
|
||||
friendCheck = nullptr;
|
||||
|
||||
delete friendResult;
|
||||
friendResult = nullptr;
|
||||
|
||||
bool isModeratorApproved = propertyEntry->getBoolean(10);
|
||||
|
||||
if (!isModeratorApproved && entity->GetGMLevel() >= GAME_MASTER_LEVEL_LEAD_MODERATOR) {
|
||||
propertyName = "[AWAITING APPROVAL]";
|
||||
propertyDescription = "[AWAITING APPROVAL]";
|
||||
isModeratorApproved = true;
|
||||
}
|
||||
|
||||
bool isAlt = false;
|
||||
// Query to determine whether this property is an alt character of the entity.
|
||||
auto isAltQuery = Database::CreatePreppedStmt("SELECT id FROM charinfo where account_id in (SELECT account_id from charinfo WHERE id = ?) AND id = ?;");
|
||||
|
||||
isAltQuery->setInt(1, character->GetID());
|
||||
isAltQuery->setInt(2, owner);
|
||||
|
||||
auto isAltQueryResults = isAltQuery->executeQuery();
|
||||
|
||||
if (isAltQueryResults->next()) {
|
||||
isAlt = true;
|
||||
}
|
||||
|
||||
delete isAltQueryResults;
|
||||
isAltQueryResults = nullptr;
|
||||
|
||||
delete isAltQuery;
|
||||
isAltQuery = nullptr;
|
||||
|
||||
entry = SetPropertyValues(entry, cloneId, ownerName, propertyName, propertyDescription, reputation, isBestFriend, isFriend, isModeratorApproved, isAlt, isOwned, privacyOption, dateLastUpdated, performanceCost);
|
||||
|
||||
entries.push_back(entry);
|
||||
}
|
||||
|
||||
delete propertyEntry;
|
||||
propertyEntry = nullptr;
|
||||
|
||||
delete propertyLookup;
|
||||
|
||||
/*
|
||||
const auto entriesSize = entries.size();
|
||||
|
||||
if (startIndex != 0 && entriesSize > startIndex)
|
||||
{
|
||||
for (size_t i = 0; i < startIndex; i++)
|
||||
{
|
||||
entries.erase(entries.begin());
|
||||
}
|
||||
}
|
||||
*/
|
||||
propertyLookup = nullptr;
|
||||
|
||||
propertyQueries[entity->GetObjectID()] = entries;
|
||||
|
||||
// Query here is to figure out whether or not to display the button to go to the next page or not.
|
||||
int32_t numberOfProperties = 0;
|
||||
|
||||
GameMessages::SendPropertySelectQuery(
|
||||
m_Parent->GetObjectID(),
|
||||
startIndex,
|
||||
entries.size() >= numResults,
|
||||
character->GetPropertyCloneID(),
|
||||
false,
|
||||
true,
|
||||
entries,
|
||||
sysAddr
|
||||
);
|
||||
auto buttonQuery = BuildQuery(entity, sortMethod, "SELECT COUNT(*) FROM properties as p JOIN charinfo as ci ON ci.prop_clone_id = p.clone_id where p.zone_id = ? AND (p.description LIKE ? OR p.name LIKE ? OR ci.name LIKE ?) AND p.privacy_option >= ? ", false);
|
||||
auto propertiesLeft = Database::CreatePreppedStmt(buttonQuery);
|
||||
|
||||
propertiesLeft->setUInt(1, this->m_MapID);
|
||||
propertiesLeft->setString(2, searchString.c_str());
|
||||
propertiesLeft->setString(3, searchString.c_str());
|
||||
propertiesLeft->setString(4, searchString.c_str());
|
||||
propertiesLeft->setInt(5, sortMethod == SORT_TYPE_FEATURED || sortMethod == SORT_TYPE_FRIENDS ? 1 : 2);
|
||||
|
||||
auto result = propertiesLeft->executeQuery();
|
||||
result->next();
|
||||
numberOfProperties = result->getInt(1);
|
||||
|
||||
delete result;
|
||||
result = nullptr;
|
||||
|
||||
delete propertiesLeft;
|
||||
propertiesLeft = nullptr;
|
||||
|
||||
GameMessages::SendPropertySelectQuery(m_Parent->GetObjectID(), startIndex, numberOfProperties - (startIndex + numResults) > 0, character->GetPropertyCloneID(), false, true, entries, sysAddr);
|
||||
}
|
@@ -1,17 +1,17 @@
|
||||
#pragma once
|
||||
|
||||
#include <map>
|
||||
|
||||
#include "Component.h"
|
||||
#include "Entity.h"
|
||||
#include "EntityManager.h"
|
||||
#include "GameMessages.h"
|
||||
#include "Component.h"
|
||||
#include <map>
|
||||
|
||||
/**
|
||||
* Represents the launch pad that's used to select and browse properties
|
||||
*/
|
||||
class PropertyEntranceComponent : public Component
|
||||
{
|
||||
public:
|
||||
class PropertyEntranceComponent : public Component {
|
||||
public:
|
||||
static const uint32_t ComponentType = COMPONENT_TYPE_PROPERTY_ENTRANCE;
|
||||
explicit PropertyEntranceComponent(uint32_t componentID, Entity* parent);
|
||||
|
||||
@@ -24,11 +24,11 @@ public:
|
||||
/**
|
||||
* Handles the event triggered when the entity selects a property to visit and makes the entity to there
|
||||
* @param entity the entity that triggered the event
|
||||
* @param index the clone ID of the property to visit
|
||||
* @param index the index of the property property
|
||||
* @param returnToZone whether or not the entity wishes to go back to the launch zone
|
||||
* @param sysAddr the address to send gamemessage responses to
|
||||
*/
|
||||
void OnEnterProperty(Entity* entity, uint32_t index, bool returnToZone, const SystemAddress &sysAddr);
|
||||
void OnEnterProperty(Entity* entity, uint32_t index, bool returnToZone, const SystemAddress& sysAddr);
|
||||
|
||||
/**
|
||||
* Handles a request for information on available properties when an entity lands on the property
|
||||
@@ -38,23 +38,13 @@ public:
|
||||
* @param playerOwn only query properties owned by the entity
|
||||
* @param updateUi unused
|
||||
* @param numResults unused
|
||||
* @param reputation unused
|
||||
* @param lReputationTime unused
|
||||
* @param sortMethod unused
|
||||
* @param startIndex the minimum index to start the query off
|
||||
* @param filterText property names to search for
|
||||
* @param sysAddr the address to send gamemessage responses to
|
||||
*/
|
||||
void OnPropertyEntranceSync(Entity* entity,
|
||||
bool includeNullAddress,
|
||||
bool includeNullDescription,
|
||||
bool playerOwn,
|
||||
bool updateUi,
|
||||
int32_t numResults,
|
||||
int32_t reputation,
|
||||
int32_t sortMethod,
|
||||
int32_t startIndex,
|
||||
std::string filterText,
|
||||
const SystemAddress &sysAddr);
|
||||
void OnPropertyEntranceSync(Entity* entity, bool includeNullAddress, bool includeNullDescription, bool playerOwn, bool updateUi, int32_t numResults, int32_t lReputationTime, int32_t sortMethod, int32_t startIndex, std::string filterText, const SystemAddress& sysAddr);
|
||||
|
||||
/**
|
||||
* Returns the name of this property
|
||||
@@ -68,8 +58,11 @@ public:
|
||||
*/
|
||||
[[nodiscard]] LWOMAPID GetMapID() const { return m_MapID; };
|
||||
|
||||
private:
|
||||
PropertySelectQueryProperty SetPropertyValues(PropertySelectQueryProperty property, LWOCLONEID cloneId = LWOCLONEID_INVALID, std::string ownerName = "", std::string propertyName = "", std::string propertyDescription = "", float reputation = 0, bool isBFF = false, bool isFriend = false, bool isModeratorApproved = false, bool isAlt = false, bool isOwned = false, uint32_t privacyOption = 0, uint32_t timeLastUpdated = 0, float performanceCost = 0.0f);
|
||||
|
||||
std::string BuildQuery(Entity* entity, int32_t sortMethod, std::string customQuery = "", bool wantLimits = true);
|
||||
|
||||
private:
|
||||
/**
|
||||
* Cache of property information that was queried for property launched, indexed by property ID
|
||||
*/
|
||||
@@ -84,4 +77,13 @@ private:
|
||||
* The base map ID for this property (Avant Grove, etc).
|
||||
*/
|
||||
LWOMAPID m_MapID;
|
||||
|
||||
enum ePropertySortType : int32_t {
|
||||
SORT_TYPE_FRIENDS = 0,
|
||||
SORT_TYPE_REPUTATION = 1,
|
||||
SORT_TYPE_RECENT = 3,
|
||||
SORT_TYPE_FEATURED = 5
|
||||
};
|
||||
|
||||
std::string baseQueryForProperties = "SELECT p.* FROM properties as p JOIN charinfo as ci ON ci.prop_clone_id = p.clone_id where p.zone_id = ? AND (p.description LIKE ? OR p.name LIKE ? OR ci.name LIKE ?) AND p.privacy_option >= ? ";
|
||||
};
|
||||
|
@@ -68,13 +68,18 @@ PropertyManagementComponent::PropertyManagementComponent(Entity* parent) : Compo
|
||||
this->owner = propertyEntry->getUInt64(2);
|
||||
this->owner = GeneralUtils::SetBit(this->owner, OBJECT_BIT_CHARACTER);
|
||||
this->owner = GeneralUtils::SetBit(this->owner, OBJECT_BIT_PERSISTENT);
|
||||
this->clone_Id = propertyEntry->getInt(2);
|
||||
this->propertyName = propertyEntry->getString(5).c_str();
|
||||
this->propertyDescription = propertyEntry->getString(6).c_str();
|
||||
this->privacyOption = static_cast<PropertyPrivacyOption>(propertyEntry->getUInt(9));
|
||||
this->claimedTime = propertyEntry->getUInt64(13);
|
||||
this->moderatorRequested = propertyEntry->getInt(10) == 0 && rejectionReason == "" && privacyOption == PropertyPrivacyOption::Public;
|
||||
this->LastUpdatedTime = propertyEntry->getUInt64(11);
|
||||
this->claimedTime = propertyEntry->getUInt64(12);
|
||||
this->rejectionReason = propertyEntry->getString(13).asStdString();
|
||||
this->reputation = propertyEntry->getUInt(14);
|
||||
|
||||
Load();
|
||||
}
|
||||
}
|
||||
|
||||
delete propertyLookup;
|
||||
}
|
||||
@@ -152,12 +157,18 @@ void PropertyManagementComponent::SetPrivacyOption(PropertyPrivacyOption value)
|
||||
value = PropertyPrivacyOption::Private;
|
||||
}
|
||||
|
||||
if (value == PropertyPrivacyOption::Public && privacyOption != PropertyPrivacyOption::Public) {
|
||||
rejectionReason = "";
|
||||
moderatorRequested = true;
|
||||
}
|
||||
privacyOption = value;
|
||||
|
||||
auto* propertyUpdate = Database::CreatePreppedStmt("UPDATE properties SET privacy_option = ? WHERE id = ?;");
|
||||
auto* propertyUpdate = Database::CreatePreppedStmt("UPDATE properties SET privacy_option = ?, rejection_reason = ?, mod_approved = ? WHERE id = ?;");
|
||||
|
||||
propertyUpdate->setInt(1, static_cast<int32_t>(value));
|
||||
propertyUpdate->setInt64(2, propertyId);
|
||||
propertyUpdate->setString(2, "");
|
||||
propertyUpdate->setInt(3, 0);
|
||||
propertyUpdate->setInt64(4, propertyId);
|
||||
|
||||
propertyUpdate->executeUpdate();
|
||||
}
|
||||
@@ -181,38 +192,46 @@ void PropertyManagementComponent::UpdatePropertyDetails(std::string name, std::s
|
||||
OnQueryPropertyData(GetOwner(), UNASSIGNED_SYSTEM_ADDRESS);
|
||||
}
|
||||
|
||||
void PropertyManagementComponent::Claim(const LWOOBJID playerId)
|
||||
bool PropertyManagementComponent::Claim(const LWOOBJID playerId)
|
||||
{
|
||||
if (owner != LWOOBJID_EMPTY)
|
||||
{
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
SetOwnerId(playerId);
|
||||
|
||||
auto* zone = dZoneManager::Instance()->GetZone();
|
||||
|
||||
const auto& worldId = zone->GetZoneID();
|
||||
const auto zoneId = worldId.GetMapID();
|
||||
const auto cloneId = worldId.GetCloneID();
|
||||
|
||||
auto* entity = EntityManager::Instance()->GetEntity(playerId);
|
||||
|
||||
auto* user = entity->GetParentUser();
|
||||
|
||||
auto character = entity->GetCharacter();
|
||||
if (!character) return false;
|
||||
|
||||
auto* zone = dZoneManager::Instance()->GetZone();
|
||||
|
||||
const auto& worldId = zone->GetZoneID();
|
||||
const auto propertyZoneId = worldId.GetMapID();
|
||||
const auto propertyCloneId = worldId.GetCloneID();
|
||||
|
||||
const auto playerCloneId = character->GetPropertyCloneID();
|
||||
|
||||
// If we are not on our clone do not allow us to claim the property
|
||||
if (propertyCloneId != playerCloneId) return false;
|
||||
|
||||
SetOwnerId(playerId);
|
||||
|
||||
propertyId = ObjectIDManager::GenerateRandomObjectID();
|
||||
|
||||
|
||||
auto* insertion = Database::CreatePreppedStmt(
|
||||
"INSERT INTO properties"
|
||||
"(id, owner_id, template_id, clone_id, name, description, rent_amount, rent_due, privacy_option, last_updated, time_claimed, rejection_reason, reputation, zone_id)"
|
||||
"VALUES (?, ?, ?, ?, ?, '', 0, 0, 0, UNIX_TIMESTAMP(), UNIX_TIMESTAMP(), '', 0, ?)"
|
||||
"(id, owner_id, template_id, clone_id, name, description, rent_amount, rent_due, privacy_option, last_updated, time_claimed, rejection_reason, reputation, zone_id, performance_cost)"
|
||||
"VALUES (?, ?, ?, ?, ?, '', 0, 0, 0, UNIX_TIMESTAMP(), UNIX_TIMESTAMP(), '', 0, ?, 0.0)"
|
||||
);
|
||||
insertion->setUInt64(1, propertyId);
|
||||
insertion->setUInt64(2, (uint32_t) playerId);
|
||||
insertion->setUInt(3, templateId);
|
||||
insertion->setUInt64(4, cloneId);
|
||||
insertion->setUInt64(4, playerCloneId);
|
||||
insertion->setString(5, zone->GetZoneName().c_str());
|
||||
insertion->setInt(6, zoneId);
|
||||
insertion->setInt(6, propertyZoneId);
|
||||
|
||||
// Try and execute the query, print an error if it fails.
|
||||
try
|
||||
@@ -224,12 +243,14 @@ void PropertyManagementComponent::Claim(const LWOOBJID playerId)
|
||||
Game::logger->Log("PropertyManagementComponent", "Failed to execute query: (%s)!\n", exception.what());
|
||||
|
||||
throw exception;
|
||||
return false;
|
||||
}
|
||||
|
||||
auto* zoneControlObject = dZoneManager::Instance()->GetZoneControlObject();
|
||||
for (CppScripts::Script* script : CppScripts::GetEntityScripts(zoneControlObject)) {
|
||||
script->OnZonePropertyRented(zoneControlObject, entity);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void PropertyManagementComponent::OnStartBuilding()
|
||||
@@ -275,6 +296,8 @@ void PropertyManagementComponent::OnFinishBuilding()
|
||||
SetPrivacyOption(originalPrivacyOption);
|
||||
|
||||
UpdateApprovedStatus(false);
|
||||
|
||||
Save();
|
||||
}
|
||||
|
||||
void PropertyManagementComponent::UpdateModelPosition(const LWOOBJID id, const NiPoint3 position, NiQuaternion rotation)
|
||||
@@ -388,6 +411,9 @@ void PropertyManagementComponent::UpdateModelPosition(const LWOOBJID id, const N
|
||||
|
||||
EntityManager::Instance()->GetZoneControlEntity()->OnZonePropertyModelPlaced(entity);
|
||||
});
|
||||
// Progress place model missions
|
||||
auto missionComponent = entity->GetComponent<MissionComponent>();
|
||||
if (missionComponent != nullptr) missionComponent->Progress(MissionTaskType::MISSION_TASK_TYPE_PLACE_MODEL, 0);
|
||||
}
|
||||
|
||||
void PropertyManagementComponent::DeleteModel(const LWOOBJID id, const int deleteReason)
|
||||
@@ -465,7 +491,7 @@ void PropertyManagementComponent::DeleteModel(const LWOOBJID id, const int delet
|
||||
settings.push_back(propertyObjectID);
|
||||
settings.push_back(modelType);
|
||||
|
||||
inventoryComponent->AddItem(6662, 1, HIDDEN, settings, LWOOBJID_EMPTY, false, false, spawnerId, INVALID, 13, false, -1);
|
||||
inventoryComponent->AddItem(6662, 1, eLootSourceType::LOOT_SOURCE_DELETION, eInventoryType::HIDDEN, settings, LWOOBJID_EMPTY, false, false, spawnerId);
|
||||
auto* item = inventoryComponent->FindItemBySubKey(spawnerId);
|
||||
|
||||
if (item == nullptr) {
|
||||
@@ -500,7 +526,7 @@ void PropertyManagementComponent::DeleteModel(const LWOOBJID id, const int delet
|
||||
return;
|
||||
}
|
||||
|
||||
inventoryComponent->AddItem(model->GetLOT(), 1, INVALID, {}, LWOOBJID_EMPTY, false);
|
||||
inventoryComponent->AddItem(model->GetLOT(), 1, eLootSourceType::LOOT_SOURCE_DELETION, INVALID, {}, LWOOBJID_EMPTY, false);
|
||||
|
||||
auto* item = inventoryComponent->FindItemByLot(model->GetLOT());
|
||||
|
||||
@@ -682,9 +708,12 @@ void PropertyManagementComponent::Save()
|
||||
auto* remove = Database::CreatePreppedStmt("DELETE FROM properties_contents WHERE id = ?;");
|
||||
|
||||
lookup->setUInt64(1, propertyId);
|
||||
|
||||
auto* lookupResult = lookup->executeQuery();
|
||||
|
||||
sql::ResultSet* lookupResult = nullptr;
|
||||
try {
|
||||
lookupResult = lookup->executeQuery();
|
||||
} catch (sql::SQLException& ex) {
|
||||
Game::logger->Log("PropertyManagementComponent", "lookup error %s\n", ex.what());
|
||||
}
|
||||
std::vector<LWOOBJID> present;
|
||||
|
||||
while (lookupResult->next())
|
||||
@@ -727,8 +756,11 @@ void PropertyManagementComponent::Save()
|
||||
insertion->setDouble(9, rotation.y);
|
||||
insertion->setDouble(10, rotation.z);
|
||||
insertion->setDouble(11, rotation.w);
|
||||
|
||||
insertion->execute();
|
||||
try {
|
||||
insertion->execute();
|
||||
} catch (sql::SQLException& ex) {
|
||||
Game::logger->Log("PropertyManagementComponent", "Error inserting into properties_contents. Error %s\n", ex.what());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -741,8 +773,11 @@ void PropertyManagementComponent::Save()
|
||||
update->setDouble(7, rotation.w);
|
||||
|
||||
update->setInt64(8, id);
|
||||
|
||||
update->executeUpdate();
|
||||
try {
|
||||
update->executeUpdate();
|
||||
} catch (sql::SQLException& ex) {
|
||||
Game::logger->Log("PropertyManagementComponent", "Error updating properties_contents. Error: %s\n", ex.what());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -754,8 +789,11 @@ void PropertyManagementComponent::Save()
|
||||
}
|
||||
|
||||
remove->setInt64(1, id);
|
||||
|
||||
remove->execute();
|
||||
try {
|
||||
remove->execute();
|
||||
} catch (sql::SQLException& ex) {
|
||||
Game::logger->Log("PropertyManagementComponent", "Error removing from properties_contents. Error %s\n", ex.what());
|
||||
}
|
||||
}
|
||||
|
||||
auto* removeUGC = Database::CreatePreppedStmt("DELETE FROM ugc WHERE id NOT IN (SELECT ugc_id FROM properties_contents);");
|
||||
@@ -779,7 +817,7 @@ PropertyManagementComponent* PropertyManagementComponent::Instance()
|
||||
return instance;
|
||||
}
|
||||
|
||||
void PropertyManagementComponent::OnQueryPropertyData(Entity* originator, const SystemAddress& sysAddr, LWOOBJID author) const
|
||||
void PropertyManagementComponent::OnQueryPropertyData(Entity* originator, const SystemAddress& sysAddr, LWOOBJID author)
|
||||
{
|
||||
if (author == LWOOBJID_EMPTY) {
|
||||
author = m_Parent->GetObjectID();
|
||||
@@ -818,18 +856,44 @@ void PropertyManagementComponent::OnQueryPropertyData(Entity* originator, const
|
||||
description = propertyDescription;
|
||||
claimed = claimedTime;
|
||||
privacy = static_cast<char>(this->privacyOption);
|
||||
}
|
||||
if (moderatorRequested) {
|
||||
auto checkStatus = Database::CreatePreppedStmt("SELECT rejection_reason, mod_approved FROM properties WHERE id = ?;");
|
||||
|
||||
checkStatus->setInt64(1, propertyId);
|
||||
|
||||
auto result = checkStatus->executeQuery();
|
||||
|
||||
result->next();
|
||||
|
||||
const auto reason = result->getString(1).asStdString();;
|
||||
const auto modApproved = result->getInt(2);
|
||||
if (reason != "") {
|
||||
moderatorRequested = false;
|
||||
rejectionReason = reason;
|
||||
} else if (reason == "" && modApproved == 1) {
|
||||
moderatorRequested = false;
|
||||
rejectionReason = "";
|
||||
} else {
|
||||
moderatorRequested = true;
|
||||
rejectionReason = "";
|
||||
}
|
||||
}
|
||||
}
|
||||
message.moderatorRequested = moderatorRequested;
|
||||
message.reputation = reputation;
|
||||
message.LastUpdatedTime = LastUpdatedTime;
|
||||
message.OwnerId = ownerId;
|
||||
message.OwnerName = ownerName;
|
||||
message.Name = name;
|
||||
message.Description = description;
|
||||
message.ClaimedTime = claimed;
|
||||
message.PrivacyOption = privacy;
|
||||
|
||||
message.cloneId = clone_Id;
|
||||
message.rejectionReason = rejectionReason;
|
||||
message.Paths = GetPaths();
|
||||
|
||||
SendDownloadPropertyData(author, message, UNASSIGNED_SYSTEM_ADDRESS);
|
||||
// send rejection here?
|
||||
}
|
||||
|
||||
void PropertyManagementComponent::OnUse(Entity* originator)
|
||||
|
@@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <chrono>
|
||||
#include "Entity.h"
|
||||
#include "Component.h"
|
||||
|
||||
@@ -40,7 +41,7 @@ public:
|
||||
* @param sysAddr the address to send game message responses to
|
||||
* @param author optional explicit ID for the property, if not set defaults to the originator
|
||||
*/
|
||||
void OnQueryPropertyData(Entity* originator, const SystemAddress& sysAddr, LWOOBJID author = LWOOBJID_EMPTY) const;
|
||||
void OnQueryPropertyData(Entity* originator, const SystemAddress& sysAddr, LWOOBJID author = LWOOBJID_EMPTY);
|
||||
|
||||
/**
|
||||
* Handles an OnUse event, telling the client who owns this property, etc.
|
||||
@@ -100,8 +101,10 @@ public:
|
||||
/**
|
||||
* Makes this property owned by the passed player ID, storing it in the database
|
||||
* @param playerId the ID of the entity that claimed the property
|
||||
*
|
||||
* @return If the claim is successful return true.
|
||||
*/
|
||||
void Claim(LWOOBJID playerId);
|
||||
bool Claim(LWOOBJID playerId);
|
||||
|
||||
/**
|
||||
* Event triggered when the owner of the property starts building, will kick other entities out
|
||||
@@ -158,6 +161,8 @@ public:
|
||||
*/
|
||||
const std::map<LWOOBJID, LWOOBJID>& GetModels() const;
|
||||
|
||||
LWOCLONEID GetCloneId() { return clone_Id; };
|
||||
|
||||
private:
|
||||
/**
|
||||
* This
|
||||
@@ -182,7 +187,7 @@ private:
|
||||
/**
|
||||
* The time since this property was claimed
|
||||
*/
|
||||
uint64_t claimedTime = 0;
|
||||
uint64_t claimedTime = std::chrono::duration_cast<std::chrono::seconds>(std::chrono::system_clock::now().time_since_epoch()).count();
|
||||
|
||||
/**
|
||||
* The models that are placed on this property
|
||||
@@ -194,11 +199,36 @@ private:
|
||||
*/
|
||||
std::string propertyName = "";
|
||||
|
||||
/**
|
||||
* The clone ID of this property
|
||||
*/
|
||||
LWOCLONEID clone_Id = 0;
|
||||
|
||||
/**
|
||||
* Whether a moderator was requested
|
||||
*/
|
||||
bool moderatorRequested = false;
|
||||
|
||||
/**
|
||||
* The rejection reason for the property
|
||||
*/
|
||||
std::string rejectionReason = "";
|
||||
|
||||
/**
|
||||
* The description of this property
|
||||
*/
|
||||
std::string propertyDescription = "";
|
||||
|
||||
/**
|
||||
* The reputation of this property
|
||||
*/
|
||||
uint32_t reputation = 0;
|
||||
|
||||
/**
|
||||
* The last time this property was updated
|
||||
*/
|
||||
uint32_t LastUpdatedTime = std::chrono::duration_cast<std::chrono::seconds>(std::chrono::system_clock::now().time_since_epoch()).count();
|
||||
|
||||
/**
|
||||
* Determines which players may visit this property
|
||||
*/
|
||||
|
@@ -41,13 +41,16 @@ void PropertyVendorComponent::OnBuyFromVendor(Entity* originator, const bool con
|
||||
{
|
||||
if (PropertyManagementComponent::Instance() == nullptr) return;
|
||||
|
||||
if (PropertyManagementComponent::Instance()->Claim(originator->GetObjectID()) == false) {
|
||||
Game::logger->Log("PropertyVendorComponent", "FAILED TO CLAIM PROPERTY. PLAYER ID IS %llu\n", originator->GetObjectID());
|
||||
return;
|
||||
}
|
||||
|
||||
GameMessages::SendPropertyRentalResponse(m_Parent->GetObjectID(), 0, 0, 0, 0, originator->GetSystemAddress());
|
||||
|
||||
auto* controller = dZoneManager::Instance()->GetZoneControlObject();
|
||||
|
||||
controller->OnFireEventServerSide(m_Parent, "propertyRented");
|
||||
|
||||
PropertyManagementComponent::Instance()->Claim(originator->GetObjectID());
|
||||
|
||||
PropertyManagementComponent::Instance()->SetOwner(originator);
|
||||
|
||||
|
@@ -15,6 +15,7 @@
|
||||
#include "Player.h"
|
||||
#include "PossessableComponent.h"
|
||||
#include "PossessorComponent.h"
|
||||
#include "RacingTaskParam.h"
|
||||
#include "Spawner.h"
|
||||
#include "VehiclePhysicsComponent.h"
|
||||
#include "dServer.h"
|
||||
@@ -52,6 +53,11 @@ RacingControlComponent::RacingControlComponent(Entity *parent)
|
||||
m_MainWorld = 1200;
|
||||
break;
|
||||
|
||||
case 1261:
|
||||
m_ActivityID = 60;
|
||||
m_MainWorld = 1260;
|
||||
break;
|
||||
|
||||
case 1303:
|
||||
m_ActivityID = 39;
|
||||
m_MainWorld = 1300;
|
||||
@@ -401,18 +407,21 @@ void RacingControlComponent::HandleMessageBoxResponse(Entity *player,
|
||||
|
||||
auto *missionComponent = player->GetComponent<MissionComponent>();
|
||||
|
||||
if (missionComponent != nullptr) {
|
||||
missionComponent->Progress(
|
||||
MissionTaskType::MISSION_TASK_TYPE_RACING, 0, 13); // Enter race
|
||||
missionComponent->Progress(
|
||||
MissionTaskType::MISSION_TASK_TYPE_RACING, data->finished,
|
||||
1); // Finish with rating, one track
|
||||
missionComponent->Progress(
|
||||
MissionTaskType::MISSION_TASK_TYPE_RACING, data->finished,
|
||||
15); // Finish with rating, multiple tracks
|
||||
missionComponent->Progress(
|
||||
MissionTaskType::MISSION_TASK_TYPE_RACING, data->smashedTimes,
|
||||
10); // Safe driver type missions
|
||||
if (missionComponent == nullptr) return;
|
||||
|
||||
missionComponent->Progress(MissionTaskType::MISSION_TASK_TYPE_RACING, 0, (LWOOBJID)RacingTaskParam::RACING_TASK_PARAM_COMPETED_IN_RACE); // Progress task for competing in a race
|
||||
missionComponent->Progress(MissionTaskType::MISSION_TASK_TYPE_RACING, data->smashedTimes, (LWOOBJID)RacingTaskParam::RACING_TASK_PARAM_SAFE_DRIVER); // Finish a race without being smashed.
|
||||
|
||||
// If solo racing is enabled OR if there are 3 players in the race, progress placement tasks.
|
||||
if(m_SoloRacing || m_LoadedPlayers > 2) {
|
||||
missionComponent->Progress(MissionTaskType::MISSION_TASK_TYPE_RACING, data->finished, (LWOOBJID)RacingTaskParam::RACING_TASK_PARAM_FINISH_WITH_PLACEMENT); // Finish in 1st place on a race
|
||||
if(data->finished == 1) {
|
||||
missionComponent->Progress(MissionTaskType::MISSION_TASK_TYPE_RACING, dZoneManager::Instance()->GetZone()->GetWorldID(), (LWOOBJID)RacingTaskParam::RACING_TASK_PARAM_FIRST_PLACE_MULTIPLE_TRACKS); // Finish in 1st place on multiple tracks.
|
||||
missionComponent->Progress(MissionTaskType::MISSION_TASK_TYPE_RACING, dZoneManager::Instance()->GetZone()->GetWorldID(), (LWOOBJID)RacingTaskParam::RACING_TASK_PARAM_WIN_RACE_IN_WORLD); // Finished first place in specific world.
|
||||
}
|
||||
if (data->finished == m_LoadedPlayers) {
|
||||
missionComponent->Progress(MissionTaskType::MISSION_TASK_TYPE_RACING, dZoneManager::Instance()->GetZone()->GetWorldID(), (LWOOBJID)RacingTaskParam::RACING_TASK_PARAM_LAST_PLACE_FINISH); // Finished first place in specific world.
|
||||
}
|
||||
}
|
||||
} else if (id == "ACT_RACE_EXIT_THE_RACE?" || id == "Exit") {
|
||||
auto *vehicle = EntityManager::Instance()->GetEntity(data->vehicleID);
|
||||
@@ -809,9 +818,7 @@ void RacingControlComponent::Update(float deltaTime) {
|
||||
|
||||
// Reached the start point, lapped
|
||||
if (respawnIndex == 0) {
|
||||
time_t lapTime =
|
||||
std::time(nullptr) -
|
||||
(player.lap == 1 ? m_StartTime : player.lapTime);
|
||||
time_t lapTime = std::time(nullptr) - (player.lap == 1 ? m_StartTime : player.lapTime);
|
||||
|
||||
// Cheating check
|
||||
if (lapTime < 40) {
|
||||
@@ -833,10 +840,9 @@ void RacingControlComponent::Update(float deltaTime) {
|
||||
playerEntity->GetComponent<MissionComponent>();
|
||||
|
||||
if (missionComponent != nullptr) {
|
||||
// Lap time
|
||||
missionComponent->Progress(
|
||||
MissionTaskType::MISSION_TASK_TYPE_RACING,
|
||||
(lapTime)*1000, 2);
|
||||
|
||||
// Progress lap time tasks
|
||||
missionComponent->Progress(MissionTaskType::MISSION_TASK_TYPE_RACING, (lapTime)*1000, (LWOOBJID)RacingTaskParam::RACING_TASK_PARAM_LAP_TIME);
|
||||
|
||||
if (player.lap == 3) {
|
||||
m_Finished++;
|
||||
@@ -852,15 +858,11 @@ void RacingControlComponent::Update(float deltaTime) {
|
||||
raceTime, raceTime * 1000);
|
||||
|
||||
// Entire race time
|
||||
missionComponent->Progress(
|
||||
MissionTaskType::MISSION_TASK_TYPE_RACING,
|
||||
(raceTime)*1000, 3);
|
||||
missionComponent->Progress(MissionTaskType::MISSION_TASK_TYPE_RACING, (raceTime)*1000, (LWOOBJID)RacingTaskParam::RACING_TASK_PARAM_TOTAL_TRACK_TIME);
|
||||
|
||||
auto *characterComponent =
|
||||
playerEntity->GetComponent<CharacterComponent>();
|
||||
auto *characterComponent = playerEntity->GetComponent<CharacterComponent>();
|
||||
if (characterComponent != nullptr) {
|
||||
characterComponent->TrackRaceCompleted(m_Finished ==
|
||||
1);
|
||||
characterComponent->TrackRaceCompleted(m_Finished == 1);
|
||||
}
|
||||
|
||||
// TODO: Figure out how to update the GUI leaderboard.
|
||||
|
@@ -146,7 +146,7 @@ public:
|
||||
void HandleMessageBoxResponse(Entity* player, const std::string& id);
|
||||
|
||||
/**
|
||||
* Get the reacing data from a player's LWOOBJID.
|
||||
* Get the racing data from a player's LWOOBJID.
|
||||
*/
|
||||
RacingPlayerInfo* GetPlayerData(LWOOBJID playerID);
|
||||
|
||||
@@ -230,7 +230,7 @@ private:
|
||||
std::vector<LWOOBJID> m_LobbyPlayers;
|
||||
|
||||
/**
|
||||
* The number of players that have fi nished the race
|
||||
* The number of players that have finished the race
|
||||
*/
|
||||
uint32_t m_Finished;
|
||||
|
||||
|
@@ -45,7 +45,14 @@ void RebuildComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitia
|
||||
|
||||
outBitStream->Write(false);
|
||||
}
|
||||
|
||||
// If build state is completed and we've already serialized once in the completed state,
|
||||
// don't serializing this component anymore as this will cause the build to jump again.
|
||||
// If state changes, serialization will begin again.
|
||||
if (!m_StateDirty && m_State == REBUILD_COMPLETED) {
|
||||
outBitStream->Write0();
|
||||
outBitStream->Write0();
|
||||
return;
|
||||
}
|
||||
// BEGIN Scripted Activity
|
||||
outBitStream->Write1();
|
||||
|
||||
@@ -79,6 +86,7 @@ void RebuildComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitia
|
||||
outBitStream->Write(m_ActivatorPosition);
|
||||
outBitStream->Write(m_RepositionPlayer);
|
||||
}
|
||||
m_StateDirty = false;
|
||||
}
|
||||
|
||||
void RebuildComponent::Update(float deltaTime) {
|
||||
@@ -139,7 +147,6 @@ void RebuildComponent::Update(float deltaTime) {
|
||||
}
|
||||
|
||||
if (m_Timer >= m_ResetTime) {
|
||||
m_Builder = LWOOBJID_EMPTY;
|
||||
|
||||
GameMessages::SendDieNoImplCode(m_Parent, LWOOBJID_EMPTY, LWOOBJID_EMPTY, eKillType::VIOLENT, u"", 0.0f, 0.0f, 0.0f, false, true);
|
||||
|
||||
@@ -380,11 +387,11 @@ void RebuildComponent::StartRebuild(Entity* user) {
|
||||
|
||||
EntityManager::Instance()->SerializeEntity(user);
|
||||
|
||||
GameMessages::SendRebuildNotifyState(m_Parent, m_State, eRebuildState::REBUILD_COMPLETED, user->GetObjectID());
|
||||
GameMessages::SendRebuildNotifyState(m_Parent, m_State, eRebuildState::REBUILD_BUILDING, user->GetObjectID());
|
||||
GameMessages::SendEnableRebuild(m_Parent, true, false, false, eFailReason::REASON_NOT_GIVEN, 0.0f, user->GetObjectID());
|
||||
|
||||
m_State = eRebuildState::REBUILD_BUILDING;
|
||||
|
||||
m_StateDirty = true;
|
||||
EntityManager::Instance()->SerializeEntity(m_Parent);
|
||||
|
||||
auto* movingPlatform = m_Parent->GetComponent<MovingPlatformComponent>();
|
||||
@@ -421,17 +428,18 @@ void RebuildComponent::CompleteRebuild(Entity* user) {
|
||||
EntityManager::Instance()->SerializeEntity(user);
|
||||
|
||||
GameMessages::SendRebuildNotifyState(m_Parent, m_State, eRebuildState::REBUILD_COMPLETED, user->GetObjectID());
|
||||
GameMessages::SendEnableRebuild(m_Parent, false, true, false, eFailReason::REASON_NOT_GIVEN, m_ResetTime, user->GetObjectID());
|
||||
GameMessages::SendPlayFXEffect(m_Parent, 507, u"create", "BrickFadeUpVisCompleteEffect", LWOOBJID_EMPTY, 0.4f, 1.0f, true);
|
||||
GameMessages::SendEnableRebuild(m_Parent, false, false, true, eFailReason::REASON_NOT_GIVEN, m_ResetTime, user->GetObjectID());
|
||||
GameMessages::SendTerminateInteraction(user->GetObjectID(), eTerminateType::FROM_INTERACTION, m_Parent->GetObjectID());
|
||||
|
||||
|
||||
m_State = eRebuildState::REBUILD_COMPLETED;
|
||||
m_StateDirty = true;
|
||||
m_Timer = 0.0f;
|
||||
m_DrainedImagination = 0;
|
||||
|
||||
EntityManager::Instance()->SerializeEntity(m_Parent);
|
||||
|
||||
GameMessages::SendPlayFXEffect(m_Parent, 507, u"create", "BrickFadeUpVisCompleteEffect", LWOOBJID_EMPTY, 0.4f, 1.0f, true);
|
||||
GameMessages::SendTerminateInteraction(user->GetObjectID(), eTerminateType::FROM_INTERACTION, m_Parent->GetObjectID());
|
||||
|
||||
// Removes extra item requirements, isn't live accurate.
|
||||
// In live, all items were removed at the start of the quickbuild, then returned if it was cancelled.
|
||||
// TODO: fix?
|
||||
@@ -455,8 +463,6 @@ void RebuildComponent::CompleteRebuild(Entity* user) {
|
||||
LootGenerator::Instance().DropActivityLoot(builder, m_Parent, m_ActivityId, 1);
|
||||
}
|
||||
|
||||
m_Builder = LWOOBJID_EMPTY;
|
||||
|
||||
// Notify scripts
|
||||
for (auto* script : CppScripts::GetEntityScripts(m_Parent)) {
|
||||
script->OnRebuildComplete(m_Parent, user);
|
||||
@@ -484,6 +490,7 @@ void RebuildComponent::CompleteRebuild(Entity* user) {
|
||||
character->SetPlayerFlag(flagNumber, true);
|
||||
}
|
||||
}
|
||||
GameMessages::SendPlayAnimation(user, u"rebuild-celebrate", 1.09f);
|
||||
}
|
||||
|
||||
void RebuildComponent::ResetRebuild(bool failed) {
|
||||
@@ -500,12 +507,11 @@ void RebuildComponent::ResetRebuild(bool failed) {
|
||||
GameMessages::SendRebuildNotifyState(m_Parent, m_State, eRebuildState::REBUILD_RESETTING, LWOOBJID_EMPTY);
|
||||
|
||||
m_State = eRebuildState::REBUILD_RESETTING;
|
||||
m_StateDirty = true;
|
||||
m_Timer = 0.0f;
|
||||
m_TimerIncomplete = 0.0f;
|
||||
m_ShowResetEffect = false;
|
||||
m_DrainedImagination = 0;
|
||||
|
||||
m_Builder = LWOOBJID_EMPTY;
|
||||
|
||||
EntityManager::Instance()->SerializeEntity(m_Parent);
|
||||
|
||||
@@ -540,6 +546,7 @@ void RebuildComponent::CancelRebuild(Entity* entity, eFailReason failReason, boo
|
||||
|
||||
// Now update the component itself
|
||||
m_State = eRebuildState::REBUILD_INCOMPLETE;
|
||||
m_StateDirty = true;
|
||||
|
||||
// Notify scripts and possible subscribers
|
||||
for (auto* script : CppScripts::GetEntityScripts(m_Parent))
|
||||
|
@@ -216,7 +216,11 @@ public:
|
||||
*/
|
||||
void CancelRebuild(Entity* builder, eFailReason failReason, bool skipChecks = false);
|
||||
private:
|
||||
|
||||
/**
|
||||
* Whether or not the quickbuild state has been changed since we last serialized it.
|
||||
*/
|
||||
bool m_StateDirty = true;
|
||||
|
||||
/**
|
||||
* The state the rebuild is currently in
|
||||
*/
|
||||
@@ -235,7 +239,7 @@ private:
|
||||
/**
|
||||
* The position that the rebuild activator is spawned at
|
||||
*/
|
||||
NiPoint3 m_ActivatorPosition {};
|
||||
NiPoint3 m_ActivatorPosition = NiPoint3::ZERO;
|
||||
|
||||
/**
|
||||
* The entity that represents the rebuild activator
|
||||
|
32
dGame/dComponents/RocketLaunchLupComponent.cpp
Normal file
32
dGame/dComponents/RocketLaunchLupComponent.cpp
Normal file
@@ -0,0 +1,32 @@
|
||||
#include "RocketLaunchLupComponent.h"
|
||||
#include "RocketLaunchpadControlComponent.h"
|
||||
#include "InventoryComponent.h"
|
||||
#include "CharacterComponent.h"
|
||||
|
||||
RocketLaunchLupComponent::RocketLaunchLupComponent(Entity* parent) : Component(parent) {
|
||||
m_Parent = parent;
|
||||
std::string zoneString = GeneralUtils::UTF16ToWTF8(m_Parent->GetVar<std::u16string>(u"MultiZoneIDs"));
|
||||
std::stringstream ss(zoneString);
|
||||
for (int i; ss >> i;) {
|
||||
m_LUPWorlds.push_back(i);
|
||||
if (ss.peek() == ';')
|
||||
ss.ignore();
|
||||
}
|
||||
}
|
||||
|
||||
RocketLaunchLupComponent::~RocketLaunchLupComponent() {}
|
||||
|
||||
void RocketLaunchLupComponent::OnUse(Entity* originator) {
|
||||
auto* rocket = originator->GetComponent<CharacterComponent>()->RocketEquip(originator);
|
||||
if (!rocket) return;
|
||||
|
||||
// the LUP world menu is just the property menu, the client knows how to handle it
|
||||
GameMessages::SendPropertyEntranceBegin(m_Parent->GetObjectID(), m_Parent->GetSystemAddress());
|
||||
}
|
||||
|
||||
void RocketLaunchLupComponent::OnSelectWorld(Entity* originator, uint32_t index) {
|
||||
auto* rocketLaunchpadControlComponent = m_Parent->GetComponent<RocketLaunchpadControlComponent>();
|
||||
if (!rocketLaunchpadControlComponent) return;
|
||||
|
||||
rocketLaunchpadControlComponent->Launch(originator, m_LUPWorlds[index], 0);
|
||||
}
|
39
dGame/dComponents/RocketLaunchLupComponent.h
Normal file
39
dGame/dComponents/RocketLaunchLupComponent.h
Normal file
@@ -0,0 +1,39 @@
|
||||
#pragma once
|
||||
|
||||
#include "Entity.h"
|
||||
#include "GameMessages.h"
|
||||
#include "Component.h"
|
||||
|
||||
/**
|
||||
* Component that handles the LUP/WBL rocket launchpad that can be interacted with to travel to WBL worlds.
|
||||
*
|
||||
*/
|
||||
class RocketLaunchLupComponent : public Component {
|
||||
public:
|
||||
static const uint32_t ComponentType = eReplicaComponentType::COMPONENT_TYPE_ROCKET_LAUNCH_LUP;
|
||||
|
||||
/**
|
||||
* Constructor for this component, builds the m_LUPWorlds vector
|
||||
* @param parent parent that contains this component
|
||||
*/
|
||||
RocketLaunchLupComponent(Entity* parent);
|
||||
~RocketLaunchLupComponent() override;
|
||||
|
||||
/**
|
||||
* Handles an OnUse event from some entity, preparing it for launch to some other world
|
||||
* @param originator the entity that triggered the event
|
||||
*/
|
||||
void OnUse(Entity* originator) override;
|
||||
|
||||
/**
|
||||
* Handles an OnUse event from some entity, preparing it for launch to some other world
|
||||
* @param originator the entity that triggered the event
|
||||
* @param index index of the world that was selected
|
||||
*/
|
||||
void OnSelectWorld(Entity* originator, uint32_t index);
|
||||
private:
|
||||
/**
|
||||
* vector of the LUP World Zone IDs, built from CDServer's LUPZoneIDs table
|
||||
*/
|
||||
std::vector<LWOMAPID> m_LUPWorlds {};
|
||||
};
|
@@ -13,6 +13,7 @@
|
||||
#include "ChatPackets.h"
|
||||
#include "MissionComponent.h"
|
||||
#include "PropertyEntranceComponent.h"
|
||||
#include "RocketLaunchLupComponent.h"
|
||||
#include "dServer.h"
|
||||
#include "dMessageIdentifiers.h"
|
||||
#include "PacketUtils.h"
|
||||
@@ -40,19 +41,7 @@ RocketLaunchpadControlComponent::~RocketLaunchpadControlComponent() {
|
||||
delete m_AltPrecondition;
|
||||
}
|
||||
|
||||
void RocketLaunchpadControlComponent::RocketEquip(Entity* entity, LWOOBJID rocketID) {
|
||||
if (m_PlayersInRadius.find(entity->GetObjectID()) != m_PlayersInRadius.end()) {
|
||||
Launch(entity, rocketID);
|
||||
|
||||
//Go ahead and save the player
|
||||
//This causes a double-save, but it should prevent players from not being saved
|
||||
//before the next world server starts loading their data.
|
||||
if (entity->GetCharacter())
|
||||
entity->GetCharacter()->SaveXMLToDatabase();
|
||||
}
|
||||
}
|
||||
|
||||
void RocketLaunchpadControlComponent::Launch(Entity* originator, LWOOBJID optionalRocketID, LWOMAPID mapId, LWOCLONEID cloneId) {
|
||||
void RocketLaunchpadControlComponent::Launch(Entity* originator, LWOMAPID mapId, LWOCLONEID cloneId) {
|
||||
auto zone = mapId == LWOMAPID_INVALID ? m_TargetZone : mapId;
|
||||
|
||||
if (zone == 0)
|
||||
@@ -60,53 +49,22 @@ void RocketLaunchpadControlComponent::Launch(Entity* originator, LWOOBJID option
|
||||
return;
|
||||
}
|
||||
|
||||
TellMasterToPrepZone(zone);
|
||||
|
||||
// This also gets triggered by a proximity monitor + item equip, I will set that up when havok is ready
|
||||
auto* inventoryComponent = originator->GetComponent<InventoryComponent>();
|
||||
auto* characterComponent = originator->GetComponent<CharacterComponent>();
|
||||
|
||||
auto* character = originator->GetCharacter();
|
||||
|
||||
if (inventoryComponent == nullptr || characterComponent == nullptr || character == nullptr) {
|
||||
if (!characterComponent || !character) return;
|
||||
|
||||
auto* rocket = characterComponent->GetRocket(originator);
|
||||
if (!rocket) {
|
||||
Game::logger->Log("RocketLaunchpadControlComponent", "Unable to find rocket!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
// Select the rocket
|
||||
|
||||
Item* rocket = nullptr;
|
||||
|
||||
if (optionalRocketID != LWOOBJID_EMPTY)
|
||||
{
|
||||
rocket = inventoryComponent->FindItemById(optionalRocketID);
|
||||
}
|
||||
|
||||
if (rocket == nullptr)
|
||||
{
|
||||
rocket = inventoryComponent->FindItemById(characterComponent->GetLastRocketItemID());
|
||||
}
|
||||
|
||||
if (rocket == nullptr)
|
||||
{
|
||||
rocket = inventoryComponent->FindItemByLot(6416);
|
||||
}
|
||||
|
||||
if (rocket == nullptr)
|
||||
{
|
||||
Game::logger->Log("RocketLaunchpadControlComponent", "Unable to find rocket (%llu)!\n", optionalRocketID);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (rocket->GetConfig().empty()) // Sanity check
|
||||
{
|
||||
rocket->SetCount(0, false, false);
|
||||
|
||||
return;
|
||||
}
|
||||
// we have the ability to launch, so now we prep the zone
|
||||
TellMasterToPrepZone(zone);
|
||||
|
||||
// Achievement unlocked: "All zones unlocked"
|
||||
|
||||
if (!m_AltLandingScene.empty() && m_AltPrecondition->Check(originator)) {
|
||||
character->SetTargetScene(m_AltLandingScene);
|
||||
}
|
||||
@@ -114,27 +72,6 @@ void RocketLaunchpadControlComponent::Launch(Entity* originator, LWOOBJID option
|
||||
character->SetTargetScene(m_TargetScene);
|
||||
}
|
||||
|
||||
if (characterComponent) {
|
||||
for (LDFBaseData* data : rocket->GetConfig()) {
|
||||
if (data->GetKey() == u"assemblyPartLOTs") {
|
||||
std::string newRocketStr;
|
||||
for (char character : data->GetValueAsString()) {
|
||||
if (character == '+') {
|
||||
newRocketStr.push_back(';');
|
||||
}
|
||||
else {
|
||||
newRocketStr.push_back(character);
|
||||
}
|
||||
}
|
||||
newRocketStr.push_back(';');
|
||||
characterComponent->SetLastRocketConfig(GeneralUtils::ASCIIToUTF16(newRocketStr));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Store the last used rocket item's ID
|
||||
characterComponent->SetLastRocketItemID(rocket->GetId());
|
||||
|
||||
characterComponent->UpdatePlayerStatistic(RocketsUsed);
|
||||
|
||||
character->SaveXMLToDatabase();
|
||||
@@ -142,24 +79,32 @@ void RocketLaunchpadControlComponent::Launch(Entity* originator, LWOOBJID option
|
||||
SetSelectedMapId(originator->GetObjectID(), zone);
|
||||
|
||||
GameMessages::SendFireEventClientSide(m_Parent->GetObjectID(), originator->GetSystemAddress(), u"RocketEquipped", rocket->GetId(), cloneId, -1, originator->GetObjectID());
|
||||
|
||||
rocket->Equip(true);
|
||||
|
||||
|
||||
GameMessages::SendChangeObjectWorldState(rocket->GetId(), WORLDSTATE_ATTACHED, UNASSIGNED_SYSTEM_ADDRESS);
|
||||
|
||||
EntityManager::Instance()->SerializeEntity(originator);
|
||||
}
|
||||
|
||||
void RocketLaunchpadControlComponent::OnUse(Entity* originator) {
|
||||
// If we are have the property or the LUP component, we don't want to immediately launch
|
||||
// instead we let their OnUse handlers do their things
|
||||
// which components of an Object have their OnUse called when using them
|
||||
// so we don't need to call it here
|
||||
auto* propertyEntrance = m_Parent->GetComponent<PropertyEntranceComponent>();
|
||||
|
||||
if (propertyEntrance != nullptr)
|
||||
{
|
||||
propertyEntrance->OnUse(originator);
|
||||
|
||||
if (propertyEntrance) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto* rocketLaunchLUP = m_Parent->GetComponent<RocketLaunchLupComponent>();
|
||||
if (rocketLaunchLUP) {
|
||||
return;
|
||||
}
|
||||
|
||||
// No rocket no launch
|
||||
auto* rocket = originator->GetComponent<CharacterComponent>()->RocketEquip(originator);
|
||||
if (!rocket) {
|
||||
return;
|
||||
}
|
||||
Launch(originator);
|
||||
}
|
||||
|
||||
|
@@ -22,21 +22,13 @@ public:
|
||||
RocketLaunchpadControlComponent(Entity* parent, int rocketId);
|
||||
~RocketLaunchpadControlComponent() override;
|
||||
|
||||
/**
|
||||
* Launches the passed entity using the passed rocket and saves their data
|
||||
* @param entity the entity to launch
|
||||
* @param rocketID the ID of the rocket to use
|
||||
*/
|
||||
void RocketEquip(Entity* entity, LWOOBJID rocketID);
|
||||
|
||||
/**
|
||||
* Launches some entity to another world
|
||||
* @param originator the entity to launch
|
||||
* @param optionalRocketID the ID of the rocket to launch
|
||||
* @param mapId the world to go to
|
||||
* @param cloneId the clone ID (for properties)
|
||||
*/
|
||||
void Launch(Entity* originator, LWOOBJID optionalRocketID = LWOOBJID_EMPTY, LWOMAPID mapId = LWOMAPID_INVALID, LWOCLONEID cloneId = LWOCLONEID_INVALID);
|
||||
void Launch(Entity* originator, LWOMAPID mapId = LWOMAPID_INVALID, LWOCLONEID cloneId = LWOCLONEID_INVALID);
|
||||
|
||||
/**
|
||||
* Handles an OnUse event from some entity, preparing it for launch to some other world
|
||||
|
@@ -28,7 +28,7 @@ ScriptedActivityComponent::ScriptedActivityComponent(Entity* parent, int activit
|
||||
|
||||
const auto mapID = m_ActivityInfo.instanceMapID;
|
||||
|
||||
if ((mapID == 1203 || mapID == 1303 || mapID == 1403) && Game::config->GetValue("solo_racing") == "1") {
|
||||
if ((mapID == 1203 || mapID == 1261 || mapID == 1303 || mapID == 1403) && Game::config->GetValue("solo_racing") == "1") {
|
||||
m_ActivityInfo.minTeamSize = 1;
|
||||
m_ActivityInfo.minTeams = 1;
|
||||
}
|
||||
|
@@ -17,6 +17,17 @@ SimplePhysicsComponent::SimplePhysicsComponent(uint32_t componentID, Entity* par
|
||||
m_Position = m_Parent->GetDefaultPosition();
|
||||
m_Rotation = m_Parent->GetDefaultRotation();
|
||||
m_IsDirty = true;
|
||||
|
||||
const auto& climbable_type = m_Parent->GetVar<std::u16string>(u"climbable");
|
||||
if (climbable_type == u"wall") {
|
||||
SetClimbableType(eClimbableType::CLIMBABLE_TYPE_WALL);
|
||||
} else if (climbable_type == u"ladder") {
|
||||
SetClimbableType(eClimbableType::CLIMBABLE_TYPE_LADDER);
|
||||
} else if (climbable_type == u"wallstick") {
|
||||
SetClimbableType(eClimbableType::CLIMBABLE_TYPE_WALL_STICK);
|
||||
} else {
|
||||
SetClimbableType(eClimbableType::CLIMBABLE_TYPE_NOT);
|
||||
}
|
||||
}
|
||||
|
||||
SimplePhysicsComponent::~SimplePhysicsComponent() {
|
||||
@@ -24,10 +35,10 @@ SimplePhysicsComponent::~SimplePhysicsComponent() {
|
||||
|
||||
void SimplePhysicsComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags) {
|
||||
if (bIsInitialUpdate) {
|
||||
outBitStream->Write0(); // climbable
|
||||
outBitStream->Write<int32_t>(0); // climbableType
|
||||
outBitStream->Write(m_ClimbableType != eClimbableType::CLIMBABLE_TYPE_NOT);
|
||||
outBitStream->Write(m_ClimbableType);
|
||||
}
|
||||
|
||||
|
||||
outBitStream->Write(m_DirtyVelocity || bIsInitialUpdate);
|
||||
if (m_DirtyVelocity || bIsInitialUpdate) {
|
||||
outBitStream->Write(m_Velocity);
|
||||
@@ -46,7 +57,7 @@ void SimplePhysicsComponent::Serialize(RakNet::BitStream* outBitStream, bool bIs
|
||||
{
|
||||
outBitStream->Write0();
|
||||
}
|
||||
|
||||
|
||||
outBitStream->Write(m_IsDirty || bIsInitialUpdate);
|
||||
if (m_IsDirty || bIsInitialUpdate) {
|
||||
outBitStream->Write(m_Position.x);
|
||||
@@ -66,7 +77,7 @@ uint32_t SimplePhysicsComponent::GetPhysicsMotionState() const
|
||||
return m_PhysicsMotionState;
|
||||
}
|
||||
|
||||
void SimplePhysicsComponent::SetPhysicsMotionState(uint32_t value)
|
||||
void SimplePhysicsComponent::SetPhysicsMotionState(uint32_t value)
|
||||
{
|
||||
m_PhysicsMotionState = value;
|
||||
}
|
||||
|
@@ -14,16 +14,24 @@
|
||||
|
||||
class Entity;
|
||||
|
||||
enum class eClimbableType : int32_t {
|
||||
CLIMBABLE_TYPE_NOT = 0,
|
||||
CLIMBABLE_TYPE_LADDER,
|
||||
CLIMBABLE_TYPE_WALL,
|
||||
CLIMBABLE_TYPE_WALL_STICK
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Component that serializes locations of entities to the client
|
||||
*/
|
||||
class SimplePhysicsComponent : public Component {
|
||||
public:
|
||||
static const uint32_t ComponentType = COMPONENT_TYPE_SIMPLE_PHYSICS;
|
||||
|
||||
|
||||
SimplePhysicsComponent(uint32_t componentID, Entity* parent);
|
||||
~SimplePhysicsComponent() override;
|
||||
|
||||
|
||||
void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags);
|
||||
|
||||
/**
|
||||
@@ -86,6 +94,18 @@ public:
|
||||
*/
|
||||
void SetPhysicsMotionState(uint32_t value);
|
||||
|
||||
/**
|
||||
* Returns the ClimbableType of this entity
|
||||
* @return the ClimbableType of this entity
|
||||
*/
|
||||
const eClimbableType& GetClimabbleType() { return m_ClimbableType; }
|
||||
|
||||
/**
|
||||
* Sets the ClimbableType of this entity
|
||||
* @param value the ClimbableType to set
|
||||
*/
|
||||
void SetClimbableType(const eClimbableType& value) { m_ClimbableType = value; }
|
||||
|
||||
private:
|
||||
|
||||
/**
|
||||
@@ -122,6 +142,11 @@ private:
|
||||
* The current physics motion state
|
||||
*/
|
||||
uint32_t m_PhysicsMotionState = 0;
|
||||
|
||||
/**
|
||||
* Whether or not the entity is climbable
|
||||
*/
|
||||
eClimbableType m_ClimbableType = eClimbableType::CLIMBABLE_TYPE_NOT;
|
||||
};
|
||||
|
||||
#endif // SIMPLEPHYSICSCOMPONENT_H
|
||||
|
@@ -25,12 +25,14 @@ ProjectileSyncEntry::ProjectileSyncEntry()
|
||||
{
|
||||
}
|
||||
|
||||
bool SkillComponent::CastPlayerSkill(const uint32_t behaviorId, const uint32_t skillUid, RakNet::BitStream* bitStream, const LWOOBJID target)
|
||||
bool SkillComponent::CastPlayerSkill(const uint32_t behaviorId, const uint32_t skillUid, RakNet::BitStream* bitStream, const LWOOBJID target, uint32_t skillID)
|
||||
{
|
||||
auto* context = new BehaviorContext(this->m_Parent->GetObjectID());
|
||||
|
||||
context->caster = m_Parent->GetObjectID();
|
||||
|
||||
context->skillID = skillID;
|
||||
|
||||
this->m_managedBehaviors.insert_or_assign(skillUid, context);
|
||||
|
||||
auto* behavior = Behavior::CreateBehavior(behaviorId);
|
||||
|
@@ -92,7 +92,7 @@ public:
|
||||
* @param bitStream the bitSteam given by the client to determine the behavior path
|
||||
* @param target the explicit target of the skill
|
||||
*/
|
||||
bool CastPlayerSkill(uint32_t behaviorId, uint32_t skillUid, RakNet::BitStream* bitStream, LWOOBJID target);
|
||||
bool CastPlayerSkill(uint32_t behaviorId, uint32_t skillUid, RakNet::BitStream* bitStream, LWOOBJID target, uint32_t skillID = 0);
|
||||
|
||||
/**
|
||||
* Continues a player skill. Should only be called when the server receives a sync message from the client.
|
||||
|
@@ -1,116 +1,131 @@
|
||||
#include "VendorComponent.h"
|
||||
#include "Game.h"
|
||||
#include "dServer.h"
|
||||
|
||||
#include <BitStream.h>
|
||||
|
||||
#include "Game.h"
|
||||
#include "dServer.h"
|
||||
|
||||
VendorComponent::VendorComponent(Entity* parent) : Component(parent) {
|
||||
auto* compRegistryTable = CDClientManager::Instance()->GetTable<CDComponentsRegistryTable>("ComponentsRegistry");
|
||||
auto* vendorComponentTable = CDClientManager::Instance()->GetTable<CDVendorComponentTable>("VendorComponent");
|
||||
auto* lootMatrixTable = CDClientManager::Instance()->GetTable<CDLootMatrixTable>("LootMatrix");
|
||||
auto* lootTableTable = CDClientManager::Instance()->GetTable<CDLootTableTable>("LootTable");
|
||||
|
||||
int componentID = compRegistryTable->GetByIDAndType(m_Parent->GetLOT(), COMPONENT_TYPE_VENDOR);
|
||||
std::vector<CDVendorComponent> vendorComps = vendorComponentTable->Query([=](CDVendorComponent entry) { return (entry.id == componentID); });
|
||||
if (vendorComps.empty()) {
|
||||
return;
|
||||
}
|
||||
m_BuyScalar = vendorComps[0].buyScalar;
|
||||
m_SellScalar = vendorComps[0].sellScalar;
|
||||
int lootMatrixID = vendorComps[0].LootMatrixIndex;
|
||||
std::vector<CDLootMatrix> lootMatrices = lootMatrixTable->Query([=](CDLootMatrix entry) { return (entry.LootMatrixIndex == lootMatrixID); });
|
||||
if (lootMatrices.empty()) {
|
||||
return;
|
||||
}
|
||||
for (const auto& lootMatrix : lootMatrices) {
|
||||
int lootTableID = lootMatrix.LootTableIndex;
|
||||
std::vector<CDLootTable> vendorItems = lootTableTable->Query([=](CDLootTable entry) { return (entry.LootTableIndex == lootTableID); });
|
||||
if (lootMatrix.maxToDrop == 0 || lootMatrix.minToDrop == 0) {
|
||||
for (CDLootTable item : vendorItems) {
|
||||
m_Inventory.insert({item.itemid, item.sortPriority});
|
||||
}
|
||||
} else {
|
||||
auto randomCount = GeneralUtils::GenerateRandomNumber<int32_t>(lootMatrix.minToDrop, lootMatrix.maxToDrop);
|
||||
|
||||
for (size_t i = 0; i < randomCount; i++) {
|
||||
if (vendorItems.empty()) {
|
||||
break;
|
||||
}
|
||||
|
||||
auto randomItemIndex = GeneralUtils::GenerateRandomNumber<int32_t>(0, vendorItems.size() - 1);
|
||||
|
||||
const auto& randomItem = vendorItems[randomItemIndex];
|
||||
|
||||
vendorItems.erase(vendorItems.begin() + randomItemIndex);
|
||||
|
||||
m_Inventory.insert({randomItem.itemid, randomItem.sortPriority});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Because I want a vendor to sell these cameras
|
||||
if (parent->GetLOT() == 13569) {
|
||||
auto randomCamera = GeneralUtils::GenerateRandomNumber<int32_t>(0, 2);
|
||||
|
||||
switch (randomCamera) {
|
||||
case 0:
|
||||
m_Inventory.insert({16253, 0}); //Grungagroid
|
||||
break;
|
||||
case 1:
|
||||
m_Inventory.insert({16254, 0}); //Hipstabrick
|
||||
break;
|
||||
case 2:
|
||||
m_Inventory.insert({16204, 0}); //Megabrixel snapshot
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//Custom code for Max vanity NPC
|
||||
if (parent->GetLOT() == 9749 && Game::server->GetZoneID() == 1201) {
|
||||
m_Inventory.clear();
|
||||
m_Inventory.insert({11909, 0}); //Top hat w frog
|
||||
m_Inventory.insert({7785, 0}); //Flash bulb
|
||||
m_Inventory.insert({12764, 0}); //Big fountain soda
|
||||
m_Inventory.insert({12241, 0}); //Hot cocoa (from fb)
|
||||
}
|
||||
SetupConstants();
|
||||
RefreshInventory(true);
|
||||
}
|
||||
|
||||
VendorComponent::~VendorComponent() = default;
|
||||
|
||||
void VendorComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags) {
|
||||
outBitStream->Write1();
|
||||
outBitStream->Write1(); // this bit is REQUIRED for vendor + mission multiinteract
|
||||
outBitStream->Write(HasCraftingStation());
|
||||
outBitStream->Write1();
|
||||
outBitStream->Write1(); // Has standard items (Required for vendors with missions.)
|
||||
outBitStream->Write(HasCraftingStation()); // Has multi use items
|
||||
}
|
||||
|
||||
void VendorComponent::OnUse(Entity* originator) {
|
||||
GameMessages::SendVendorOpenWindow(m_Parent, originator->GetSystemAddress());
|
||||
GameMessages::SendVendorStatusUpdate(m_Parent, originator->GetSystemAddress());
|
||||
GameMessages::SendVendorOpenWindow(m_Parent, originator->GetSystemAddress());
|
||||
GameMessages::SendVendorStatusUpdate(m_Parent, originator->GetSystemAddress());
|
||||
}
|
||||
|
||||
float VendorComponent::GetBuyScalar() const {
|
||||
return m_BuyScalar;
|
||||
return m_BuyScalar;
|
||||
}
|
||||
|
||||
float VendorComponent::GetSellScalar() const {
|
||||
return m_SellScalar;
|
||||
return m_SellScalar;
|
||||
}
|
||||
|
||||
void VendorComponent::SetBuyScalar(float value) {
|
||||
m_BuyScalar = value;
|
||||
m_BuyScalar = value;
|
||||
}
|
||||
|
||||
void VendorComponent::SetSellScalar(float value) {
|
||||
m_SellScalar = value;
|
||||
m_SellScalar = value;
|
||||
}
|
||||
|
||||
std::map<LOT, int>& VendorComponent::GetInventory() {
|
||||
return m_Inventory;
|
||||
return m_Inventory;
|
||||
}
|
||||
|
||||
bool VendorComponent::HasCraftingStation() {
|
||||
// As far as we know, only Umami has a crafting station
|
||||
return m_Parent->GetLOT() == 13800;
|
||||
// As far as we know, only Umami has a crafting station
|
||||
return m_Parent->GetLOT() == 13800;
|
||||
}
|
||||
|
||||
void VendorComponent::RefreshInventory(bool isCreation) {
|
||||
//Custom code for Max vanity NPC
|
||||
if (m_Parent->GetLOT() == 9749 && Game::server->GetZoneID() == 1201) {
|
||||
if (!isCreation) return;
|
||||
m_Inventory.insert({11909, 0}); //Top hat w frog
|
||||
m_Inventory.insert({7785, 0}); //Flash bulb
|
||||
m_Inventory.insert({12764, 0}); //Big fountain soda
|
||||
m_Inventory.insert({12241, 0}); //Hot cocoa (from fb)
|
||||
return;
|
||||
}
|
||||
m_Inventory.clear();
|
||||
auto* lootMatrixTable = CDClientManager::Instance()->GetTable<CDLootMatrixTable>("LootMatrix");
|
||||
std::vector<CDLootMatrix> lootMatrices = lootMatrixTable->Query([=](CDLootMatrix entry) { return (entry.LootMatrixIndex == m_LootMatrixID); });
|
||||
|
||||
if (lootMatrices.empty()) return;
|
||||
// Done with lootMatrix table
|
||||
|
||||
auto* lootTableTable = CDClientManager::Instance()->GetTable<CDLootTableTable>("LootTable");
|
||||
|
||||
for (const auto& lootMatrix : lootMatrices) {
|
||||
int lootTableID = lootMatrix.LootTableIndex;
|
||||
std::vector<CDLootTable> vendorItems = lootTableTable->Query([=](CDLootTable entry) { return (entry.LootTableIndex == lootTableID); });
|
||||
if (lootMatrix.maxToDrop == 0 || lootMatrix.minToDrop == 0) {
|
||||
for (CDLootTable item : vendorItems) {
|
||||
m_Inventory.insert({item.itemid, item.sortPriority});
|
||||
}
|
||||
} else {
|
||||
auto randomCount = GeneralUtils::GenerateRandomNumber<int32_t>(lootMatrix.minToDrop, lootMatrix.maxToDrop);
|
||||
|
||||
for (size_t i = 0; i < randomCount; i++) {
|
||||
if (vendorItems.empty()) break;
|
||||
|
||||
auto randomItemIndex = GeneralUtils::GenerateRandomNumber<int32_t>(0, vendorItems.size() - 1);
|
||||
|
||||
const auto& randomItem = vendorItems[randomItemIndex];
|
||||
|
||||
vendorItems.erase(vendorItems.begin() + randomItemIndex);
|
||||
|
||||
m_Inventory.insert({randomItem.itemid, randomItem.sortPriority});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Because I want a vendor to sell these cameras
|
||||
if (m_Parent->GetLOT() == 13569) {
|
||||
auto randomCamera = GeneralUtils::GenerateRandomNumber<int32_t>(0, 2);
|
||||
|
||||
switch (randomCamera) {
|
||||
case 0:
|
||||
m_Inventory.insert({16253, 0}); //Grungagroid
|
||||
break;
|
||||
case 1:
|
||||
m_Inventory.insert({16254, 0}); //Hipstabrick
|
||||
break;
|
||||
case 2:
|
||||
m_Inventory.insert({16204, 0}); //Megabrixel snapshot
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Callback timer to refresh this inventory.
|
||||
m_Parent->AddCallbackTimer(m_RefreshTimeSeconds, [this]() {
|
||||
RefreshInventory();
|
||||
});
|
||||
GameMessages::SendVendorStatusUpdate(m_Parent, UNASSIGNED_SYSTEM_ADDRESS);
|
||||
}
|
||||
|
||||
void VendorComponent::SetupConstants() {
|
||||
auto* compRegistryTable = CDClientManager::Instance()->GetTable<CDComponentsRegistryTable>("ComponentsRegistry");
|
||||
int componentID = compRegistryTable->GetByIDAndType(m_Parent->GetLOT(), COMPONENT_TYPE_VENDOR);
|
||||
|
||||
auto* vendorComponentTable = CDClientManager::Instance()->GetTable<CDVendorComponentTable>("VendorComponent");
|
||||
std::vector<CDVendorComponent> vendorComps = vendorComponentTable->Query([=](CDVendorComponent entry) { return (entry.id == componentID); });
|
||||
if (vendorComps.empty()) return;
|
||||
m_BuyScalar = vendorComps[0].buyScalar;
|
||||
m_SellScalar = vendorComps[0].sellScalar;
|
||||
m_RefreshTimeSeconds = vendorComps[0].refreshTimeSeconds;
|
||||
m_LootMatrixID = vendorComps[0].LootMatrixIndex;
|
||||
}
|
@@ -1,11 +1,12 @@
|
||||
#pragma once
|
||||
#ifndef VENDORCOMPONENT_H
|
||||
#define VENDORCOMPONENT_H
|
||||
|
||||
#include "RakNetTypes.h"
|
||||
#include "Entity.h"
|
||||
#include "GameMessages.h"
|
||||
#include "CDClientManager.h"
|
||||
#include "Component.h"
|
||||
#include "Entity.h"
|
||||
#include "GameMessages.h"
|
||||
#include "RakNetTypes.h"
|
||||
|
||||
/**
|
||||
* A component for vendor NPCs. A vendor sells items to the player.
|
||||
@@ -19,7 +20,7 @@ public:
|
||||
|
||||
void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags);
|
||||
|
||||
void OnUse(Entity* originator);
|
||||
void OnUse(Entity* originator) override;
|
||||
|
||||
/**
|
||||
* Gets the buy scaler
|
||||
@@ -56,17 +57,36 @@ public:
|
||||
*/
|
||||
std::map<LOT, int>& GetInventory();
|
||||
|
||||
/**
|
||||
* Refresh the inventory of this vendor.
|
||||
*/
|
||||
void RefreshInventory(bool isCreation = false);
|
||||
|
||||
/**
|
||||
* Called on startup of vendor to setup the variables for the component.
|
||||
*/
|
||||
void SetupConstants();
|
||||
private:
|
||||
/**
|
||||
* The buy scaler.
|
||||
* The buy scalar.
|
||||
*/
|
||||
float m_BuyScalar;
|
||||
|
||||
/**
|
||||
* The sell scaler.
|
||||
* The sell scalar.
|
||||
*/
|
||||
float m_SellScalar;
|
||||
|
||||
/**
|
||||
* The refresh time of this vendors' inventory.
|
||||
*/
|
||||
float m_RefreshTimeSeconds;
|
||||
|
||||
/**
|
||||
* Loot matrix id of this vendor.
|
||||
*/
|
||||
uint32_t m_LootMatrixID;
|
||||
|
||||
/**
|
||||
* The list of items the vendor sells.
|
||||
*/
|
||||
|
Reference in New Issue
Block a user