diff --git a/dGame/dComponents/DestroyableComponent.cpp b/dGame/dComponents/DestroyableComponent.cpp index d832ef93..aab4d670 100644 --- a/dGame/dComponents/DestroyableComponent.cpp +++ b/dGame/dComponents/DestroyableComponent.cpp @@ -30,6 +30,7 @@ #include "PossessorComponent.h" #include "InventoryComponent.h" #include "dZoneManager.h" +#include "WorldConfig.h" DestroyableComponent::DestroyableComponent(Entity* parent) : Component(parent) { m_iArmor = 0; @@ -765,20 +766,16 @@ void DestroyableComponent::Smash(const LWOOBJID source, const eKillType killType uint64_t coinsTotal = character->GetCoins(); if (coinsTotal > 0) { - uint64_t coinsToLoose = 1; + const uint64_t minCoinsToLose = dZoneManager::Instance()->GetWorldConfig()->coinsLostOnDeathMin; + const uint64_t maxCoinsToLose = dZoneManager::Instance()->GetWorldConfig()->coinsLostOnDeathMax; + const float coinPercentageToLose = dZoneManager::Instance()->GetWorldConfig()->coinsLostOnDeathPercent; - if (coinsTotal >= 200) { - float hundreth = (coinsTotal / 100.0f); - coinsToLoose = static_cast(hundreth); - } + uint64_t coinsToLose = std::max(static_cast(coinsTotal * coinPercentageToLose), minCoinsToLose); + coinsToLose = std::min(maxCoinsToLose, coinsToLose); - if (coinsToLoose > 10000) { - coinsToLoose = 10000; - } + coinsTotal -= coinsToLose; - coinsTotal -= coinsToLoose; - - LootGenerator::Instance().DropLoot(m_Parent, m_Parent, -1, coinsToLoose, coinsToLoose); + LootGenerator::Instance().DropLoot(m_Parent, m_Parent, -1, coinsToLose, coinsToLose); character->SetCoins(coinsTotal, eLootSourceType::LOOT_SOURCE_PICKUP); } } diff --git a/dGame/dMission/Mission.cpp b/dGame/dMission/Mission.cpp index 61b41992..a1ae724a 100644 --- a/dGame/dMission/Mission.cpp +++ b/dGame/dMission/Mission.cpp @@ -18,6 +18,7 @@ #include "dZoneManager.h" #include "InventoryComponent.h" #include "Database.h" +#include "WorldConfig.h" Mission::Mission(MissionComponent* missionComponent, const uint32_t missionId) { m_MissionComponent = missionComponent; @@ -435,9 +436,9 @@ void Mission::YieldRewards() { int32_t coinsToSend = 0; if (info->LegoScore > 0) { eLootSourceType lootSource = info->isMission ? eLootSourceType::LOOT_SOURCE_MISSION : eLootSourceType::LOOT_SOURCE_ACHIEVEMENT; - if (levelComponent->GetLevel() >= dZoneManager::Instance()->GetMaxLevel()) { + if (levelComponent->GetLevel() >= dZoneManager::Instance()->GetWorldConfig()->levelCap) { // Since the character is at the level cap we reward them with coins instead of UScore. - coinsToSend += info->LegoScore * dZoneManager::Instance()->GetLevelCapCurrencyConversion(); + coinsToSend += info->LegoScore * dZoneManager::Instance()->GetWorldConfig()->levelCapCurrencyConversion; } else { characterComponent->SetUScore(characterComponent->GetUScore() + info->LegoScore); GameMessages::SendModifyLEGOScore(entity, entity->GetSystemAddress(), info->LegoScore, lootSource); diff --git a/dGame/dUtilities/Mail.cpp b/dGame/dUtilities/Mail.cpp index 7047972f..a4ac63e1 100644 --- a/dGame/dUtilities/Mail.cpp +++ b/dGame/dUtilities/Mail.cpp @@ -22,6 +22,8 @@ #include "MissionComponent.h" #include "ChatPackets.h" #include "Character.h" +#include "dZoneManager.h" +#include "WorldConfig.h" void Mail::SendMail(const Entity* recipient, const std::string& subject, const std::string& body, const LOT attachment, const uint16_t attachmentCount) { @@ -191,7 +193,7 @@ void Mail::HandleSendMail(RakNet::BitStream* packet, const SystemAddress& sysAdd uint32_t itemID = static_cast(attachmentID); LOT itemLOT = 0; //Inventory::InventoryType itemType; - int mailCost = 25; + int mailCost = dZoneManager::Instance()->GetWorldConfig()->mailBaseFee; int stackSize = 0; auto inv = static_cast(entity->GetComponent(COMPONENT_TYPE_INVENTORY)); Item* item = nullptr; @@ -199,7 +201,7 @@ void Mail::HandleSendMail(RakNet::BitStream* packet, const SystemAddress& sysAdd if (itemID > 0 && attachmentCount > 0 && inv) { item = inv->FindItemById(attachmentID); if (item) { - mailCost += (item->GetInfo().baseValue * 0.1f); + mailCost += (item->GetInfo().baseValue * dZoneManager::Instance()->GetWorldConfig()->mailPercentAttachmentFee); stackSize = item->GetCount(); itemLOT = item->GetLot(); } else { diff --git a/dZoneManager/WorldConfig.h b/dZoneManager/WorldConfig.h new file mode 100644 index 00000000..a98433a1 --- /dev/null +++ b/dZoneManager/WorldConfig.h @@ -0,0 +1,67 @@ +#ifndef __WORLDCONFIG__H__ +#define __WORLDCONFIG__H__ + +#include +#include + +struct WorldConfig { + int32_t worldConfigID{}; //! Primary key for WorlcConfig table + float peGravityValue{}; //! Unknown + float peBroadphaseWorldSize{}; //! Unknown + float peGameObjScaleFactor{}; //! Unknown + float characterRotationSpeed{}; //! The players' rotation speed + float characterWalkForwardSpeed{}; //! The players' walk forward speed + float characterWalkBackwardSpeed{}; //! The players' walk backwards speed + float characterWalkStrafeSpeed{}; //! The players' strafe speed + float characterWalkStrafeForwardSpeed{}; //! The players' walk strafe forward speed + float characterWalkStrafeBackwardSpeed{}; //! The players' walk strage backwards speed + float characterRunBackwardSpeed{}; //! The players' run backwards speed + float characterRunStrafeSpeed{}; //! The players' run strafe speed + float characterRunStrafeForwardSpeed{}; //! The players' run strafe forward speed + float characterRunStrafeBackwardSpeed{}; //! The players' run strage backwards speed + float globalCooldown{}; //! The global ability cooldown + float characterGroundedTime{}; //! Unknown + float characterGroundedSpeed{}; //! Unknown + float globalImmunityTime{}; //! Unknown + float characterMaxSlope{}; //! Unknown + float defaultRespawnTime{}; //! Unknown + float missionTooltipTimeout{}; + float vendorBuyMultiplier{}; //! The buy scalar for buying from vendors + float petFollowRadius{}; //! The players' pet follow radius + float characterEyeHeight{}; //! The players' eye height + float flightVerticalVelocity{}; //! Unknown + float flightAirspeed{}; //! Unknown + float flightFuelRatio{}; //! Unknown + float flightMaxAirspeed{}; //! Unknown + float fReputationPerVote{}; //! Unknown + int32_t propertyCloneLimit{}; //! Unknown + int32_t defaultHomespaceTemplate{}; //! Unknown + float coinsLostOnDeathPercent{}; //! The percentage of coins to lose on a player death + int32_t coinsLostOnDeathMin{}; //! The minimum number of coins to lose on a player death + int32_t coinsLostOnDeathMax{}; //! The maximum number of coins to lose on a player death + int32_t characterVotesPerDay{}; //! Unknown + int32_t propertyModerationRequestApprovalCost{};//! Unknown + int32_t propertyModerationRequestReviewCost{}; //! Unknown + int32_t propertyModRequestsAllowedSpike{}; //! Unknown + int32_t propertyModRequestsAllowedInterval{}; //! Unknown + int32_t propertyModRequestsAllowedTotal{}; //! Unknown + int32_t propertyModRequestsSpikeDuration{}; //! Unknown + int32_t propertyModRequestsIntervalDuration{}; //! Unknown + bool modelModerateOnCreate{}; //! Unknown + float defaultPropertyMaxHeight{}; //! Unknown + float reputationPerVoteCast{}; //! Unknown + float reputationPerVoteReceived{}; //! Unknown + int32_t showcaseTopModelConsiderationBattles{}; //! Unknown + float reputationPerBattlePromotion{}; //! Unknown + float coinsLostOnDeathMinTimeout{}; //! Unknown + float coinsLostOnDeathMaxTimeout{}; //! Unknown + int32_t mailBaseFee{}; //! The base fee to take when a player sends mail + float mailPercentAttachmentFee{}; //! The scalar multiplied by an items base cost to determine how much that item costs to be mailed + int32_t propertyReputationDelay{}; //! Unknown + int32_t levelCap{}; //! The maximum player level + std::string levelUpBehaviorEffect{}; //! Unknown + int32_t characterVersion{}; //! Unknown + int32_t levelCapCurrencyConversion{}; //! The ratio of UScore (LEGO Score) to coins +}; + +#endif //! __WORLDCONFIG__H__ diff --git a/dZoneManager/dZoneManager.cpp b/dZoneManager/dZoneManager.cpp index 102fb3af..0b7844a3 100644 --- a/dZoneManager/dZoneManager.cpp +++ b/dZoneManager/dZoneManager.cpp @@ -8,6 +8,7 @@ #include "DestroyableComponent.h" #include "GameMessages.h" #include "VanityUtilities.h" +#include "WorldConfig.h" #include #include "../dWorldServer/ObjectIDManager.h" @@ -53,6 +54,8 @@ void dZoneManager::Initialize(const LWOZONEID& zoneID) { endTime = std::chrono::duration_cast(std::chrono::steady_clock::now().time_since_epoch()).count(); + LoadWorldConfig(); + Game::logger->Log("dZoneManager", "Zone prepared in: %llu ms", (endTime - startTime)); VanityUtilities::SpawnVanity(); @@ -69,6 +72,7 @@ dZoneManager::~dZoneManager() { m_Spawners.erase(p.first); } + if (m_WorldConfig) delete m_WorldConfig; } Zone* dZoneManager::GetZone() { @@ -117,24 +121,6 @@ LWOZONEID dZoneManager::GetZoneID() const { return m_ZoneID; } -uint32_t dZoneManager::GetMaxLevel() { - if (m_MaxLevel == 0) { - auto tableData = CDClientDatabase::ExecuteQuery("SELECT LevelCap FROM WorldConfig WHERE WorldConfigID = 1 LIMIT 1;"); - m_MaxLevel = tableData.getIntField(0, -1); - tableData.finalize(); - } - return m_MaxLevel; -} - -int32_t dZoneManager::GetLevelCapCurrencyConversion() { - if (m_CurrencyConversionRate == 0) { - auto tableData = CDClientDatabase::ExecuteQuery("SELECT LevelCapCurrencyConversion FROM WorldConfig WHERE WorldConfigID = 1 LIMIT 1;"); - m_CurrencyConversionRate = tableData.getIntField(0, -1); - tableData.finalize(); - } - return m_CurrencyConversionRate; -} - void dZoneManager::Update(float deltaTime) { for (auto spawner : m_Spawners) { spawner.second->Update(deltaTime); @@ -249,3 +235,77 @@ uint32_t dZoneManager::GetUniqueMissionIdStartingValue() { } return m_UniqueMissionIdStart; } + +void dZoneManager::LoadWorldConfig() { + Game::logger->Log("dZoneManager", "Loading WorldConfig into memory"); + + auto worldConfig = CDClientDatabase::ExecuteQuery("SELECT * FROM WorldConfig;"); + + if (!m_WorldConfig) m_WorldConfig = new WorldConfig(); + + if (worldConfig.eof()) { + Game::logger->Log("dZoneManager", "WorldConfig table is empty. Is this intended?"); + return; + } + + // Now read in the giant table + m_WorldConfig->worldConfigID = worldConfig.getIntField("WorldConfigID"); + m_WorldConfig->peGravityValue = worldConfig.getFloatField("pegravityvalue"); + m_WorldConfig->peBroadphaseWorldSize = worldConfig.getFloatField("pebroadphaseworldsize"); + m_WorldConfig->peGameObjScaleFactor = worldConfig.getFloatField("pegameobjscalefactor"); + m_WorldConfig->characterRotationSpeed = worldConfig.getFloatField("character_rotation_speed"); + m_WorldConfig->characterWalkForwardSpeed = worldConfig.getFloatField("character_walk_forward_speed"); + m_WorldConfig->characterWalkBackwardSpeed = worldConfig.getFloatField("character_walk_backward_speed"); + m_WorldConfig->characterWalkStrafeSpeed = worldConfig.getFloatField("character_walk_strafe_speed"); + m_WorldConfig->characterWalkStrafeForwardSpeed = worldConfig.getFloatField("character_walk_strafe_forward_speed"); + m_WorldConfig->characterWalkStrafeBackwardSpeed = worldConfig.getFloatField("character_walk_strafe_backward_speed"); + m_WorldConfig->characterRunBackwardSpeed = worldConfig.getFloatField("character_run_backward_speed"); + m_WorldConfig->characterRunStrafeSpeed = worldConfig.getFloatField("character_run_strafe_speed"); + m_WorldConfig->characterRunStrafeForwardSpeed = worldConfig.getFloatField("character_run_strafe_forward_speed"); + m_WorldConfig->characterRunStrafeBackwardSpeed = worldConfig.getFloatField("character_run_strafe_backward_speed"); + m_WorldConfig->globalCooldown = worldConfig.getFloatField("global_cooldown"); + m_WorldConfig->characterGroundedTime = worldConfig.getFloatField("characterGroundedTime"); + m_WorldConfig->characterGroundedSpeed = worldConfig.getFloatField("characterGroundedSpeed"); + m_WorldConfig->globalImmunityTime = worldConfig.getFloatField("globalImmunityTime"); + m_WorldConfig->characterMaxSlope = worldConfig.getFloatField("character_max_slope"); + m_WorldConfig->defaultRespawnTime = worldConfig.getFloatField("defaultrespawntime"); + m_WorldConfig->missionTooltipTimeout = worldConfig.getFloatField("mission_tooltip_timeout"); + m_WorldConfig->vendorBuyMultiplier = worldConfig.getFloatField("vendor_buy_multiplier"); + m_WorldConfig->petFollowRadius = worldConfig.getFloatField("pet_follow_radius"); + m_WorldConfig->characterEyeHeight = worldConfig.getFloatField("character_eye_height"); + m_WorldConfig->flightVerticalVelocity = worldConfig.getFloatField("flight_vertical_velocity"); + m_WorldConfig->flightAirspeed = worldConfig.getFloatField("flight_airspeed"); + m_WorldConfig->flightFuelRatio = worldConfig.getFloatField("flight_fuel_ratio"); + m_WorldConfig->flightMaxAirspeed = worldConfig.getFloatField("flight_max_airspeed"); + m_WorldConfig->fReputationPerVote = worldConfig.getFloatField("fReputationPerVote"); + m_WorldConfig->propertyCloneLimit = worldConfig.getIntField("nPropertyCloneLimit"); + m_WorldConfig->defaultHomespaceTemplate = worldConfig.getIntField("defaultHomespaceTemplate"); + m_WorldConfig->coinsLostOnDeathPercent = worldConfig.getFloatField("coins_lost_on_death_percent"); + m_WorldConfig->coinsLostOnDeathMin = worldConfig.getIntField("coins_lost_on_death_min"); + m_WorldConfig->coinsLostOnDeathMax = worldConfig.getIntField("coins_lost_on_death_max"); + m_WorldConfig->characterVotesPerDay = worldConfig.getIntField("character_votes_per_day"); + m_WorldConfig->propertyModerationRequestApprovalCost = worldConfig.getIntField("property_moderation_request_approval_cost"); + m_WorldConfig->propertyModerationRequestReviewCost = worldConfig.getIntField("property_moderation_request_review_cost"); + m_WorldConfig->propertyModRequestsAllowedSpike = worldConfig.getIntField("propertyModRequestsAllowedSpike"); + m_WorldConfig->propertyModRequestsAllowedInterval = worldConfig.getIntField("propertyModRequestsAllowedInterval"); + m_WorldConfig->propertyModRequestsAllowedTotal = worldConfig.getIntField("propertyModRequestsAllowedTotal"); + m_WorldConfig->propertyModRequestsSpikeDuration = worldConfig.getIntField("propertyModRequestsSpikeDuration"); + m_WorldConfig->propertyModRequestsIntervalDuration = worldConfig.getIntField("propertyModRequestsIntervalDuration"); + m_WorldConfig->modelModerateOnCreate = worldConfig.getIntField("modelModerateOnCreate") != 0; + m_WorldConfig->defaultPropertyMaxHeight = worldConfig.getFloatField("defaultPropertyMaxHeight"); + m_WorldConfig->reputationPerVoteCast = worldConfig.getFloatField("reputationPerVoteCast"); + m_WorldConfig->reputationPerVoteReceived = worldConfig.getFloatField("reputationPerVoteReceived"); + m_WorldConfig->showcaseTopModelConsiderationBattles = worldConfig.getIntField("showcaseTopModelConsiderationBattles"); + m_WorldConfig->reputationPerBattlePromotion = worldConfig.getFloatField("reputationPerBattlePromotion"); + m_WorldConfig->coinsLostOnDeathMinTimeout = worldConfig.getFloatField("coins_lost_on_death_min_timeout"); + m_WorldConfig->coinsLostOnDeathMaxTimeout = worldConfig.getFloatField("coins_lost_on_death_max_timeout"); + m_WorldConfig->mailBaseFee = worldConfig.getIntField("mail_base_fee"); + m_WorldConfig->mailPercentAttachmentFee = worldConfig.getFloatField("mail_percent_attachment_fee"); + m_WorldConfig->propertyReputationDelay = worldConfig.getIntField("propertyReputationDelay"); + m_WorldConfig->levelCap = worldConfig.getIntField("LevelCap"); + m_WorldConfig->levelUpBehaviorEffect = worldConfig.getStringField("LevelUpBehaviorEffect"); + m_WorldConfig->characterVersion = worldConfig.getIntField("CharacterVersion"); + m_WorldConfig->levelCapCurrencyConversion = worldConfig.getIntField("LevelCapCurrencyConversion"); + worldConfig.finalize(); + Game::logger->Log("dZoneManager", "Loaded WorldConfig into memory"); +} diff --git a/dZoneManager/dZoneManager.h b/dZoneManager/dZoneManager.h index b2fef1e3..c1776e79 100644 --- a/dZoneManager/dZoneManager.h +++ b/dZoneManager/dZoneManager.h @@ -4,6 +4,8 @@ #include "Spawner.h" #include +class WorldConfig; + class dZoneManager { public: enum class dZoneNotifier { @@ -16,6 +18,12 @@ public: InvalidNotifier }; +private: + /** + * Reads the WorldConfig from the CDClientDatabase into memory + */ + void LoadWorldConfig(); + public: static dZoneManager* Instance() { if (!m_Address) { @@ -33,8 +41,6 @@ public: void NotifyZone(const dZoneNotifier& notifier, const LWOOBJID& objectID); //Notifies the zone of a certain event or command. void AddSpawner(LWOOBJID id, Spawner* spawner); LWOZONEID GetZoneID() const; - uint32_t GetMaxLevel(); - int32_t GetLevelCapCurrencyConversion(); LWOOBJID MakeSpawner(SpawnerInfo info); Spawner* GetSpawner(LWOOBJID id); void RemoveSpawner(LWOOBJID id); @@ -45,27 +51,24 @@ public: bool GetPlayerLoseCoinOnDeath() { return m_PlayerLoseCoinsOnDeath; } uint32_t GetUniqueMissionIdStartingValue(); + // The world config should not be modified by a caller. + const WorldConfig* GetWorldConfig() { + if (!m_WorldConfig) LoadWorldConfig(); + return m_WorldConfig; + }; + private: - /** - * The maximum level of the world. - */ - uint32_t m_MaxLevel = 0; - - /** - * The ratio of LEGO Score to currency when the character has hit the max level. - */ - int32_t m_CurrencyConversionRate = 0; - /** * The starting unique mission ID. */ uint32_t m_UniqueMissionIdStart = 0; static dZoneManager* m_Address; //Singleton - Zone* m_pZone; + Zone* m_pZone = nullptr; LWOZONEID m_ZoneID; bool m_PlayerLoseCoinsOnDeath; //Do players drop coins in this zone when smashed std::map m_Spawners; + WorldConfig* m_WorldConfig = nullptr; - Entity* m_ZoneControlObject; + Entity* m_ZoneControlObject = nullptr; };