mirror of
https://github.com/DarkflameUniverse/DarkflameServer.git
synced 2026-01-06 08:50:14 -06:00
Compare commits
16 Commits
vanity-rel
...
serverside
| Author | SHA1 | Date | |
|---|---|---|---|
| 1151baeeac | |||
| ee1bbb7655 | |||
| 697989f6be | |||
|
|
0ddd20e2b5 | ||
|
|
f59ca8b1da | ||
|
|
a44f998bd1 | ||
| 78d8c57752 | |||
|
|
8b270ca97a | ||
|
|
59303a232e | ||
| 8cd5bf7b8d | |||
| 8a9883c224 | |||
|
|
79ff6e7ee4 | ||
|
|
4167d98667 | ||
|
|
23c5d13151 | ||
|
|
68e5737b74 | ||
|
|
411dce7457 |
@@ -96,9 +96,6 @@ void ChatPacketHandler::HandleFriendlistRequest(Packet* packet) {
|
||||
}
|
||||
|
||||
void ChatPacketHandler::HandleFriendRequest(Packet* packet) {
|
||||
auto maxNumberOfBestFriendsAsString = Game::config->GetValue("max_number_of_best_friends");
|
||||
// If this config option doesn't exist, default to 5 which is what live used.
|
||||
auto maxNumberOfBestFriends = maxNumberOfBestFriendsAsString != "" ? std::stoi(maxNumberOfBestFriendsAsString) : 5U;
|
||||
CINSTREAM_SKIP_HEADER;
|
||||
LWOOBJID requestorPlayerID;
|
||||
inStream.Read(requestorPlayerID);
|
||||
@@ -204,11 +201,12 @@ void ChatPacketHandler::HandleFriendRequest(Packet* packet) {
|
||||
|
||||
// Only do updates if there was a change in the bff status.
|
||||
if (oldBestFriendStatus != bestFriendStatus) {
|
||||
if (requestee->countOfBestFriends >= maxNumberOfBestFriends || requestor->countOfBestFriends >= maxNumberOfBestFriends) {
|
||||
if (requestee->countOfBestFriends >= maxNumberOfBestFriends) {
|
||||
auto maxBestFriends = playerContainer.GetMaxNumberOfBestFriends();
|
||||
if (requestee->countOfBestFriends >= maxBestFriends || requestor->countOfBestFriends >= maxBestFriends) {
|
||||
if (requestee->countOfBestFriends >= maxBestFriends) {
|
||||
SendFriendResponse(requestor, requestee.get(), eAddFriendResponseType::THEIRFRIENDLISTFULL, false);
|
||||
}
|
||||
if (requestor->countOfBestFriends >= maxNumberOfBestFriends) {
|
||||
if (requestor->countOfBestFriends >= maxBestFriends) {
|
||||
SendFriendResponse(requestor, requestee.get(), eAddFriendResponseType::YOURFRIENDSLISTFULL, false);
|
||||
}
|
||||
} else {
|
||||
@@ -242,8 +240,15 @@ void ChatPacketHandler::HandleFriendRequest(Packet* packet) {
|
||||
if (requestor->sysAddr != UNASSIGNED_SYSTEM_ADDRESS) SendFriendResponse(requestor, requestee.get(), eAddFriendResponseType::WAITINGAPPROVAL, true, true);
|
||||
}
|
||||
} else {
|
||||
// Do not send this if we are requesting to be a best friend.
|
||||
SendFriendRequest(requestee.get(), requestor);
|
||||
auto maxFriends = playerContainer.GetMaxNumberOfFriends();
|
||||
if (requestee->friends.size() >= maxFriends) {
|
||||
SendFriendResponse(requestor, requestee.get(), eAddFriendResponseType::THEIRFRIENDLISTFULL, false);
|
||||
} else if (requestor->friends.size() >= maxFriends) {
|
||||
SendFriendResponse(requestor, requestee.get(), eAddFriendResponseType::YOURFRIENDSLISTFULL, false);
|
||||
} else {
|
||||
// Do not send this if we are requesting to be a best friend.
|
||||
SendFriendRequest(requestee.get(), requestor);
|
||||
}
|
||||
}
|
||||
|
||||
// If the player is actually a player and not a ghost one defined above, release it from being deleted.
|
||||
|
||||
@@ -14,10 +14,12 @@
|
||||
#include "dConfig.h"
|
||||
|
||||
PlayerContainer::PlayerContainer() {
|
||||
GeneralUtils::TryParse<uint32_t>(Game::config->GetValue("max_number_of_best_friends"), m_MaxNumberOfBestFriends);
|
||||
GeneralUtils::TryParse<uint32_t>(Game::config->GetValue("max_number_of_friends"), m_MaxNumberOfFriends);
|
||||
}
|
||||
|
||||
PlayerContainer::~PlayerContainer() {
|
||||
mPlayers.clear();
|
||||
m_Players.clear();
|
||||
}
|
||||
|
||||
TeamData::TeamData() {
|
||||
@@ -41,9 +43,9 @@ void PlayerContainer::InsertPlayer(Packet* packet) {
|
||||
inStream.Read(data->muteExpire);
|
||||
data->sysAddr = packet->systemAddress;
|
||||
|
||||
mNames[data->playerID] = GeneralUtils::UTF8ToUTF16(data->playerName);
|
||||
m_Names[data->playerID] = GeneralUtils::UTF8ToUTF16(data->playerName);
|
||||
|
||||
mPlayers.insert(std::make_pair(data->playerID, data));
|
||||
m_Players.insert(std::make_pair(data->playerID, data));
|
||||
LOG("Added user: %s (%llu), zone: %i", data->playerName.c_str(), data->playerID, data->zoneID.GetMapID());
|
||||
|
||||
auto* insertLog = Database::CreatePreppedStmt("INSERT INTO activity_log (character_id, activity, time, map_id) VALUES (?, ?, ?, ?);");
|
||||
@@ -88,7 +90,7 @@ void PlayerContainer::RemovePlayer(Packet* packet) {
|
||||
}
|
||||
|
||||
LOG("Removed user: %llu", playerID);
|
||||
mPlayers.erase(playerID);
|
||||
m_Players.erase(playerID);
|
||||
|
||||
auto* insertLog = Database::CreatePreppedStmt("INSERT INTO activity_log (character_id, activity, time, map_id) VALUES (?, ?, ?, ?);");
|
||||
|
||||
@@ -191,7 +193,7 @@ TeamData* PlayerContainer::CreateLocalTeam(std::vector<LWOOBJID> members) {
|
||||
TeamData* PlayerContainer::CreateTeam(LWOOBJID leader, bool local) {
|
||||
auto* team = new TeamData();
|
||||
|
||||
team->teamID = ++mTeamIDCounter;
|
||||
team->teamID = ++m_TeamIDCounter;
|
||||
team->leaderID = leader;
|
||||
team->local = local;
|
||||
|
||||
@@ -376,15 +378,15 @@ void PlayerContainer::UpdateTeamsOnWorld(TeamData* team, bool deleteTeam) {
|
||||
}
|
||||
|
||||
std::u16string PlayerContainer::GetName(LWOOBJID playerID) {
|
||||
const auto& pair = mNames.find(playerID);
|
||||
const auto& pair = m_Names.find(playerID);
|
||||
|
||||
if (pair == mNames.end()) return u"";
|
||||
if (pair == m_Names.end()) return u"";
|
||||
|
||||
return pair->second;
|
||||
}
|
||||
|
||||
LWOOBJID PlayerContainer::GetId(const std::u16string& playerName) {
|
||||
for (const auto& pair : mNames) {
|
||||
for (const auto& pair : m_Names) {
|
||||
if (pair.second == playerName) {
|
||||
return pair.first;
|
||||
}
|
||||
|
||||
@@ -39,13 +39,13 @@ public:
|
||||
void BroadcastMuteUpdate(LWOOBJID player, time_t time);
|
||||
|
||||
PlayerData* GetPlayerData(const LWOOBJID& playerID) {
|
||||
auto it = mPlayers.find(playerID);
|
||||
if (it != mPlayers.end()) return it->second;
|
||||
auto it = m_Players.find(playerID);
|
||||
if (it != m_Players.end()) return it->second;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
PlayerData* GetPlayerData(const std::string& playerName) {
|
||||
for (auto player : mPlayers) {
|
||||
for (auto player : m_Players) {
|
||||
if (player.second) {
|
||||
std::string pn = player.second->playerName.c_str();
|
||||
if (pn == playerName) return player.second;
|
||||
@@ -67,13 +67,17 @@ public:
|
||||
std::u16string GetName(LWOOBJID playerID);
|
||||
LWOOBJID GetId(const std::u16string& playerName);
|
||||
bool GetIsMuted(PlayerData* data);
|
||||
uint32_t GetMaxNumberOfBestFriends() { return m_MaxNumberOfBestFriends; }
|
||||
uint32_t GetMaxNumberOfFriends() { return m_MaxNumberOfFriends; }
|
||||
|
||||
std::map<LWOOBJID, PlayerData*>& GetAllPlayerData() { return mPlayers; }
|
||||
std::map<LWOOBJID, PlayerData*>& GetAllPlayerData() { return m_Players; }
|
||||
|
||||
private:
|
||||
LWOOBJID mTeamIDCounter = 0;
|
||||
std::map<LWOOBJID, PlayerData*> mPlayers;
|
||||
LWOOBJID m_TeamIDCounter = 0;
|
||||
std::map<LWOOBJID, PlayerData*> m_Players;
|
||||
std::vector<TeamData*> mTeams;
|
||||
std::unordered_map<LWOOBJID, std::u16string> mNames;
|
||||
std::unordered_map<LWOOBJID, std::u16string> m_Names;
|
||||
uint32_t m_MaxNumberOfBestFriends = 5;
|
||||
uint32_t m_MaxNumberOfFriends = 50;
|
||||
};
|
||||
|
||||
|
||||
@@ -38,13 +38,11 @@ const std::string& dConfig::GetValue(std::string key) {
|
||||
}
|
||||
|
||||
void dConfig::ProcessLine(const std::string& line) {
|
||||
auto splitLine = GeneralUtils::SplitString(line, '=');
|
||||
|
||||
if (splitLine.size() != 2) return;
|
||||
auto splitLoc = line.find('=');
|
||||
auto key = line.substr(0, splitLoc);
|
||||
auto value = line.substr(splitLoc + 1);
|
||||
|
||||
//Make sure that on Linux, we remove special characters:
|
||||
auto& key = splitLine.at(0);
|
||||
auto& value = splitLine.at(1);
|
||||
if (!value.empty() && value.at(value.size() - 1) == '\r') value.erase(value.size() - 1);
|
||||
|
||||
if (this->m_ConfigValues.find(key) != this->m_ConfigValues.end()) return;
|
||||
|
||||
@@ -36,7 +36,8 @@ enum class eWorldMessageType : uint32_t {
|
||||
HANDLE_FUNNESS,
|
||||
FAKE_PRG_CSR_MESSAGE,
|
||||
REQUEST_FREE_TRIAL_REFRESH,
|
||||
GM_SET_FREE_TRIAL_STATUS
|
||||
GM_SET_FREE_TRIAL_STATUS,
|
||||
UI_HELP_TOP_5 = 91
|
||||
};
|
||||
|
||||
#endif //!__EWORLDMESSAGETYPE__H__
|
||||
|
||||
@@ -31,7 +31,7 @@ void CDZoneTableTable::LoadValuesFromDatabase() {
|
||||
entry.smashableMaxDistance = tableData.getFloatField("smashableMaxDistance", -1.0f);
|
||||
UNUSED(entry.mixerProgram = tableData.getStringField("mixerProgram", ""));
|
||||
UNUSED(entry.clientPhysicsFramerate = tableData.getStringField("clientPhysicsFramerate", ""));
|
||||
UNUSED(entry.serverPhysicsFramerate = tableData.getStringField("serverPhysicsFramerate", ""));
|
||||
entry.serverPhysicsFramerate = tableData.getStringField("serverPhysicsFramerate", "");
|
||||
entry.zoneControlTemplate = tableData.getIntField("zoneControlTemplate", -1);
|
||||
entry.widthInChunks = tableData.getIntField("widthInChunks", -1);
|
||||
entry.heightInChunks = tableData.getIntField("heightInChunks", -1);
|
||||
@@ -40,10 +40,10 @@ void CDZoneTableTable::LoadValuesFromDatabase() {
|
||||
entry.fZoneWeight = tableData.getFloatField("fZoneWeight", -1.0f);
|
||||
UNUSED(entry.thumbnail = tableData.getStringField("thumbnail", ""));
|
||||
entry.PlayerLoseCoinsOnDeath = tableData.getIntField("PlayerLoseCoinsOnDeath", -1) == 1 ? true : false;
|
||||
UNUSED(entry.disableSaveLoc = tableData.getIntField("disableSaveLoc", -1) == 1 ? true : false);
|
||||
entry.disableSaveLoc = tableData.getIntField("disableSaveLoc", -1) == 1 ? true : false;
|
||||
entry.teamRadius = tableData.getFloatField("teamRadius", -1.0f);
|
||||
UNUSED(entry.gate_version = tableData.getStringField("gate_version", ""));
|
||||
UNUSED(entry.mountsAllowed = tableData.getIntField("mountsAllowed", -1) == 1 ? true : false);
|
||||
entry.mountsAllowed = tableData.getIntField("mountsAllowed", -1) == 1 ? true : false;
|
||||
|
||||
this->m_Entries.insert(std::make_pair(entry.zoneID, entry));
|
||||
tableData.nextRow();
|
||||
|
||||
@@ -18,7 +18,7 @@ struct CDZoneTable {
|
||||
float smashableMaxDistance; //!< The maximum smashable distance?
|
||||
UNUSED(std::string mixerProgram); //!< ???
|
||||
UNUSED(std::string clientPhysicsFramerate); //!< The client physics framerate
|
||||
UNUSED(std::string serverPhysicsFramerate); //!< The server physics framerate
|
||||
std::string serverPhysicsFramerate; //!< The server physics framerate
|
||||
unsigned int zoneControlTemplate; //!< The Zone Control template
|
||||
unsigned int widthInChunks; //!< The width of the world in chunks
|
||||
unsigned int heightInChunks; //!< The height of the world in chunks
|
||||
@@ -27,10 +27,10 @@ struct CDZoneTable {
|
||||
float fZoneWeight; //!< ???
|
||||
UNUSED(std::string thumbnail); //!< The thumbnail of the world
|
||||
bool PlayerLoseCoinsOnDeath; //!< Whether or not the user loses coins on death
|
||||
UNUSED(bool disableSaveLoc); //!< Disables the saving location?
|
||||
bool disableSaveLoc; //!< Disables the saving location?
|
||||
float teamRadius; //!< ???
|
||||
UNUSED(std::string gate_version); //!< The gate version
|
||||
UNUSED(bool mountsAllowed); //!< Whether or not mounts are allowed
|
||||
bool mountsAllowed; //!< Whether or not mounts are allowed
|
||||
};
|
||||
|
||||
class CDZoneTableTable : public CDTable<CDZoneTableTable> {
|
||||
|
||||
@@ -314,7 +314,7 @@ void Character::SaveXMLToDatabase() {
|
||||
|
||||
auto zoneInfo = Game::zoneManager->GetZone()->GetZoneID();
|
||||
// lzid garbage, binary concat of zoneID, zoneInstance and zoneClone
|
||||
if (zoneInfo.GetMapID() != 0 && zoneInfo.GetCloneID() == 0) {
|
||||
if (zoneInfo.GetMapID() != 0 && zoneInfo.GetCloneID() == 0 && !Game::zoneManager->GetDisableSaveLocation()) {
|
||||
uint64_t lzidConcat = zoneInfo.GetCloneID();
|
||||
lzidConcat = (lzidConcat << 16) | uint16_t(zoneInfo.GetInstanceID());
|
||||
lzidConcat = (lzidConcat << 16) | uint16_t(zoneInfo.GetMapID());
|
||||
|
||||
@@ -228,9 +228,9 @@ Player* Player::GetPlayer(const std::string& name) {
|
||||
|
||||
for (auto* character : characters) {
|
||||
if (!character->IsPlayer()) continue;
|
||||
|
||||
if (character->GetCharacter()->GetName() == name) {
|
||||
return static_cast<Player*>(character);
|
||||
|
||||
if (GeneralUtils::CaseInsensitiveStringCompare(name, character->GetCharacter()->GetName())) {
|
||||
return dynamic_cast<Player*>(character);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -125,7 +125,7 @@ User* UserManager::GetUser(const SystemAddress& sysAddr) {
|
||||
User* UserManager::GetUser(const std::string& username) {
|
||||
for (auto p : m_Users) {
|
||||
if (p.second) {
|
||||
if (p.second->GetUsername() == username) return p.second;
|
||||
if (GeneralUtils::CaseInsensitiveStringCompare(p.second->GetUsername(), username)) return p.second;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
#include "Game.h"
|
||||
#include "Logger.h"
|
||||
#include "EntityManager.h"
|
||||
#include "dZoneManager.h"
|
||||
#include "WorldConfig.h"
|
||||
#include "DestroyableComponent.h"
|
||||
#include "BehaviorContext.h"
|
||||
#include "eBasicAttackSuccessTypes.h"
|
||||
@@ -13,8 +15,15 @@ void BasicAttackBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bi
|
||||
|
||||
auto* destroyableComponent = entity->GetComponent<DestroyableComponent>();
|
||||
if (destroyableComponent != nullptr) {
|
||||
PlayFx(u"onhit", entity->GetObjectID());
|
||||
PlayFx(u"onhit", entity->GetObjectID()); //This damage animation doesn't seem to play consistently
|
||||
destroyableComponent->Damage(this->m_MaxDamage, context->originator, context->skillID);
|
||||
|
||||
//Handle player damage cooldown
|
||||
if (entity->IsPlayer() && !this->m_DontApplyImmune) {
|
||||
const float immunityTime = Game::zoneManager->GetWorldConfig()->globalImmunityTime;
|
||||
destroyableComponent->SetDamageCooldownTimer(immunityTime);
|
||||
LOG_DEBUG("Target targetEntity %llu took damage, setting damage cooldown timer to %f s", branch.target, immunityTime);
|
||||
}
|
||||
}
|
||||
|
||||
this->m_OnSuccess->Handle(context, bitStream, branch);
|
||||
@@ -72,6 +81,7 @@ void BasicAttackBehavior::DoHandleBehavior(BehaviorContext* context, RakNet::Bit
|
||||
}
|
||||
|
||||
if (isImmune) {
|
||||
LOG_DEBUG("Target targetEntity %llu is immune!", branch.target);
|
||||
this->m_OnFailImmune->Handle(context, bitStream, branch);
|
||||
return;
|
||||
}
|
||||
@@ -178,11 +188,15 @@ void BasicAttackBehavior::DoBehaviorCalculation(BehaviorContext* context, RakNet
|
||||
return;
|
||||
}
|
||||
|
||||
const bool isImmune = destroyableComponent->IsImmune();
|
||||
const float immunityTime = Game::zoneManager->GetWorldConfig()->globalImmunityTime;
|
||||
LOG_DEBUG("Damage cooldown timer currently %f s", destroyableComponent->GetDamageCooldownTimer());
|
||||
|
||||
const bool isImmune = (destroyableComponent->IsImmune()) || (destroyableComponent->IsCooldownImmune());
|
||||
|
||||
bitStream->Write(isImmune);
|
||||
|
||||
if (isImmune) {
|
||||
LOG_DEBUG("Target targetEntity %llu is immune!", branch.target);
|
||||
this->m_OnFailImmune->Calculate(context, bitStream, branch);
|
||||
return;
|
||||
}
|
||||
@@ -203,6 +217,12 @@ void BasicAttackBehavior::DoBehaviorCalculation(BehaviorContext* context, RakNet
|
||||
|
||||
bitStream->Write(isSuccess);
|
||||
|
||||
//Handle player damage cooldown
|
||||
if (isSuccess && targetEntity->IsPlayer() && !this->m_DontApplyImmune) {
|
||||
destroyableComponent->SetDamageCooldownTimer(immunityTime);
|
||||
LOG_DEBUG("Target targetEntity %llu took damage, setting damage cooldown timer to %f s", branch.target, immunityTime);
|
||||
}
|
||||
|
||||
eBasicAttackSuccessTypes successState = eBasicAttackSuccessTypes::FAILIMMUNE;
|
||||
if (isSuccess) {
|
||||
if (healthDamageDealt >= 1) {
|
||||
@@ -236,6 +256,8 @@ void BasicAttackBehavior::DoBehaviorCalculation(BehaviorContext* context, RakNet
|
||||
}
|
||||
|
||||
void BasicAttackBehavior::Load() {
|
||||
this->m_DontApplyImmune = GetBoolean("dont_apply_immune");
|
||||
|
||||
this->m_MinDamage = GetInt("min damage");
|
||||
if (this->m_MinDamage == 0) this->m_MinDamage = 1;
|
||||
|
||||
|
||||
@@ -10,14 +10,14 @@ public:
|
||||
/**
|
||||
* @brief Reads a 16bit short from the bitStream and when the actual behavior handling finishes with all of its branches, the bitStream
|
||||
* is then offset to after the allocated bits for this stream.
|
||||
*
|
||||
*
|
||||
*/
|
||||
void DoHandleBehavior(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch);
|
||||
|
||||
/**
|
||||
* @brief Handles a client initialized Basic Attack Behavior cast to be deserialized and verified on the server.
|
||||
*
|
||||
* @param context The Skill's Behavior context. All behaviors in the same tree share the same context
|
||||
*
|
||||
* @param context The Skill's Behavior context. All behaviors in the same tree share the same context
|
||||
* @param bitStream The bitStream to deserialize. BitStreams will always check their bounds before reading in a behavior
|
||||
* and will fail gracefully if an overread is detected.
|
||||
* @param branch The context of this specific branch of the Skill Behavior. Changes based on which branch you are going down.
|
||||
@@ -27,13 +27,13 @@ public:
|
||||
/**
|
||||
* @brief Writes a 16bit short to the bitStream and when the actual behavior calculation finishes with all of its branches, the number
|
||||
* of bits used is then written to where the 16bit short initially was.
|
||||
*
|
||||
*
|
||||
*/
|
||||
void Calculate(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) override;
|
||||
|
||||
/**
|
||||
* @brief Calculates a server initialized Basic Attack Behavior cast to be serialized to the client
|
||||
*
|
||||
*
|
||||
* @param context The Skill's Behavior context. All behaviors in the same tree share the same context
|
||||
* @param bitStream The bitStream to serialize to.
|
||||
* @param branch The context of this specific branch of the Skill Behavior. Changes based on which branch you are going down.
|
||||
@@ -44,10 +44,12 @@ public:
|
||||
* @brief Loads this Behaviors parameters from the database. For this behavior specifically:
|
||||
* max and min damage will always be the same. If min is less than max, they are both set to max.
|
||||
* If an action is not in the database, then no action is taken for that result.
|
||||
*
|
||||
*
|
||||
*/
|
||||
void Load() override;
|
||||
private:
|
||||
bool m_DontApplyImmune;
|
||||
|
||||
uint32_t m_MinDamage;
|
||||
|
||||
uint32_t m_MaxDamage;
|
||||
|
||||
@@ -64,6 +64,7 @@
|
||||
#include "FallSpeedBehavior.h"
|
||||
#include "ChangeIdleFlagsBehavior.h"
|
||||
#include "DarkInspirationBehavior.h"
|
||||
#include "ConsumeItemBehavior.h"
|
||||
|
||||
//CDClient includes
|
||||
#include "CDBehaviorParameterTable.h"
|
||||
@@ -200,7 +201,9 @@ Behavior* Behavior::CreateBehavior(const uint32_t behaviorId) {
|
||||
case BehaviorTemplates::BEHAVIOR_SKILL_EVENT:
|
||||
behavior = new SkillEventBehavior(behaviorId);
|
||||
break;
|
||||
case BehaviorTemplates::BEHAVIOR_CONSUME_ITEM: break;
|
||||
case BehaviorTemplates::BEHAVIOR_CONSUME_ITEM:
|
||||
behavior = new ConsumeItemBehavior(behaviorId);
|
||||
break;
|
||||
case BehaviorTemplates::BEHAVIOR_SKILL_CAST_FAILED:
|
||||
behavior = new SkillCastFailedBehavior(behaviorId);
|
||||
break;
|
||||
|
||||
@@ -16,6 +16,7 @@ set(DGAME_DBEHAVIORS_SOURCES "AirMovementBehavior.cpp"
|
||||
"ChangeOrientationBehavior.cpp"
|
||||
"ChargeUpBehavior.cpp"
|
||||
"ClearTargetBehavior.cpp"
|
||||
"ConsumeItemBehavior.cpp"
|
||||
"DamageAbsorptionBehavior.cpp"
|
||||
"DamageReductionBehavior.cpp"
|
||||
"DarkInspirationBehavior.cpp"
|
||||
|
||||
31
dGame/dBehaviors/ConsumeItemBehavior.cpp
Normal file
31
dGame/dBehaviors/ConsumeItemBehavior.cpp
Normal file
@@ -0,0 +1,31 @@
|
||||
#include "ConsumeItemBehavior.h"
|
||||
#include "BehaviorContext.h"
|
||||
#include "BehaviorBranchContext.h"
|
||||
#include "InventoryComponent.h"
|
||||
|
||||
void ConsumeItemBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) {
|
||||
auto action_to_cast = m_ActionNotConsumed;
|
||||
if (this->m_ConsumeLOT != -1) {
|
||||
auto caster = Game::entityManager->GetEntity(context->caster);
|
||||
if (!caster) return;
|
||||
|
||||
auto inventoryComponent = caster->GetComponent<InventoryComponent>();
|
||||
if (!inventoryComponent) return;
|
||||
|
||||
if (inventoryComponent->RemoveItem(this->m_ConsumeLOT, this->m_NumToConsume, eInventoryType::INVALID, false, true)){
|
||||
action_to_cast = m_ActionConsumed;
|
||||
}
|
||||
}
|
||||
if(action_to_cast) action_to_cast->Handle(context, bitStream, branch);
|
||||
}
|
||||
|
||||
void ConsumeItemBehavior::Sync(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) {
|
||||
Handle(context, bitStream, branch);
|
||||
}
|
||||
|
||||
void ConsumeItemBehavior::Load() {
|
||||
this->m_ConsumeLOT = GetInt("consume_lot", -1);
|
||||
this->m_NumToConsume = GetInt("num_to_consume", 1);
|
||||
this->m_ActionNotConsumed = GetAction("action_not_consumed");
|
||||
this->m_ActionConsumed = GetAction("action_consumed");
|
||||
}
|
||||
17
dGame/dBehaviors/ConsumeItemBehavior.h
Normal file
17
dGame/dBehaviors/ConsumeItemBehavior.h
Normal file
@@ -0,0 +1,17 @@
|
||||
#pragma once
|
||||
#include "Behavior.h"
|
||||
|
||||
class ConsumeItemBehavior final : public Behavior
|
||||
{
|
||||
public:
|
||||
explicit ConsumeItemBehavior(const uint32_t behaviorId) : Behavior(behaviorId) {}
|
||||
void Handle(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) override;
|
||||
void Sync(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) override;
|
||||
void Load() override;
|
||||
|
||||
private:
|
||||
LOT m_ConsumeLOT;
|
||||
uint32_t m_NumToConsume;
|
||||
Behavior* m_ActionNotConsumed;
|
||||
Behavior* m_ActionConsumed;
|
||||
};
|
||||
@@ -187,7 +187,7 @@ void ControllablePhysicsComponent::UpdateXml(tinyxml2::XMLDocument* doc) {
|
||||
|
||||
auto zoneInfo = Game::zoneManager->GetZone()->GetZoneID();
|
||||
|
||||
if (zoneInfo.GetMapID() != 0 && zoneInfo.GetCloneID() == 0) {
|
||||
if (zoneInfo.GetMapID() != 0 && zoneInfo.GetCloneID() == 0 && !Game::zoneManager->GetDisableSaveLocation()) {
|
||||
character->SetAttribute("lzx", m_Position.x);
|
||||
character->SetAttribute("lzy", m_Position.y);
|
||||
character->SetAttribute("lzz", m_Position.z);
|
||||
|
||||
@@ -73,6 +73,8 @@ DestroyableComponent::DestroyableComponent(Entity* parent) : Component(parent) {
|
||||
m_ImmuneToQuickbuildInterruptCount = 0;
|
||||
m_ImmuneToPullToPointCount = 0;
|
||||
m_DeathBehavior = -1;
|
||||
|
||||
m_DamageCooldownTimer = 0.0f;
|
||||
}
|
||||
|
||||
DestroyableComponent::~DestroyableComponent() {
|
||||
@@ -179,6 +181,10 @@ void DestroyableComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsIn
|
||||
}
|
||||
}
|
||||
|
||||
void DestroyableComponent::Update(float deltaTime) {
|
||||
m_DamageCooldownTimer -= deltaTime;
|
||||
}
|
||||
|
||||
void DestroyableComponent::LoadFromXml(tinyxml2::XMLDocument* doc) {
|
||||
tinyxml2::XMLElement* dest = doc->FirstChildElement("obj")->FirstChildElement("dest");
|
||||
if (!dest) {
|
||||
@@ -409,7 +415,7 @@ void DestroyableComponent::AddFaction(const int32_t factionID, const bool ignore
|
||||
}
|
||||
|
||||
bool DestroyableComponent::IsEnemy(const Entity* other) const {
|
||||
if (m_Parent->IsPlayer() && other->IsPlayer()){
|
||||
if (m_Parent->IsPlayer() && other->IsPlayer()) {
|
||||
auto* thisCharacterComponent = m_Parent->GetComponent<CharacterComponent>();
|
||||
if (!thisCharacterComponent) return false;
|
||||
auto* otherCharacterComponent = other->GetComponent<CharacterComponent>();
|
||||
@@ -464,6 +470,10 @@ bool DestroyableComponent::IsImmune() const {
|
||||
return m_IsGMImmune || m_ImmuneToBasicAttackCount > 0;
|
||||
}
|
||||
|
||||
bool DestroyableComponent::IsCooldownImmune() const {
|
||||
return m_DamageCooldownTimer > 0.0f;
|
||||
}
|
||||
|
||||
bool DestroyableComponent::IsKnockbackImmune() const {
|
||||
auto* characterComponent = m_Parent->GetComponent<CharacterComponent>();
|
||||
auto* inventoryComponent = m_Parent->GetComponent<InventoryComponent>();
|
||||
@@ -546,7 +556,8 @@ void DestroyableComponent::Damage(uint32_t damage, const LWOOBJID source, uint32
|
||||
return;
|
||||
}
|
||||
|
||||
if (IsImmune()) {
|
||||
if (IsImmune() || IsCooldownImmune()) {
|
||||
LOG_DEBUG("Target targetEntity %llu is immune!", m_Parent->GetObjectID()); //Immune is succesfully proc'd
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -634,9 +645,9 @@ void DestroyableComponent::Damage(uint32_t damage, const LWOOBJID source, uint32
|
||||
}
|
||||
|
||||
//check if hardcore mode is enabled
|
||||
if (Game::entityManager->GetHardcoreMode()) {
|
||||
if (Game::entityManager->GetHardcoreMode()) {
|
||||
DoHardcoreModeDrops(source);
|
||||
}
|
||||
}
|
||||
|
||||
Smash(source, eKillType::VIOLENT, u"", skillID);
|
||||
}
|
||||
@@ -796,16 +807,16 @@ void DestroyableComponent::SetFaction(int32_t factionID, bool ignoreChecks) {
|
||||
}
|
||||
|
||||
void DestroyableComponent::SetStatusImmunity(
|
||||
const eStateChangeType state,
|
||||
const bool bImmuneToBasicAttack,
|
||||
const bool bImmuneToDamageOverTime,
|
||||
const bool bImmuneToKnockback,
|
||||
const bool bImmuneToInterrupt,
|
||||
const bool bImmuneToSpeed,
|
||||
const bool bImmuneToImaginationGain,
|
||||
const bool bImmuneToImaginationLoss,
|
||||
const bool bImmuneToQuickbuildInterrupt,
|
||||
const bool bImmuneToPullToPoint) {
|
||||
const eStateChangeType state,
|
||||
const bool bImmuneToBasicAttack,
|
||||
const bool bImmuneToDamageOverTime,
|
||||
const bool bImmuneToKnockback,
|
||||
const bool bImmuneToInterrupt,
|
||||
const bool bImmuneToSpeed,
|
||||
const bool bImmuneToImaginationGain,
|
||||
const bool bImmuneToImaginationLoss,
|
||||
const bool bImmuneToQuickbuildInterrupt,
|
||||
const bool bImmuneToPullToPoint) {
|
||||
|
||||
if (state == eStateChangeType::POP) {
|
||||
if (bImmuneToBasicAttack && m_ImmuneToBasicAttackCount > 0) m_ImmuneToBasicAttackCount -= 1;
|
||||
@@ -818,7 +829,7 @@ void DestroyableComponent::SetStatusImmunity(
|
||||
if (bImmuneToQuickbuildInterrupt && m_ImmuneToQuickbuildInterruptCount > 0) m_ImmuneToQuickbuildInterruptCount -= 1;
|
||||
if (bImmuneToPullToPoint && m_ImmuneToPullToPointCount > 0) m_ImmuneToPullToPointCount -= 1;
|
||||
|
||||
} else if (state == eStateChangeType::PUSH){
|
||||
} else if (state == eStateChangeType::PUSH) {
|
||||
if (bImmuneToBasicAttack) m_ImmuneToBasicAttackCount += 1;
|
||||
if (bImmuneToDamageOverTime) m_ImmuneToDamageOverTimeCount += 1;
|
||||
if (bImmuneToKnockback) m_ImmuneToKnockbackCount += 1;
|
||||
@@ -945,7 +956,7 @@ void DestroyableComponent::AddOnHitCallback(const std::function<void(Entity*)>&
|
||||
m_OnHitCallbacks.push_back(callback);
|
||||
}
|
||||
|
||||
void DestroyableComponent::DoHardcoreModeDrops(const LWOOBJID source){
|
||||
void DestroyableComponent::DoHardcoreModeDrops(const LWOOBJID source) {
|
||||
//check if this is a player:
|
||||
if (m_Parent->IsPlayer()) {
|
||||
//remove hardcore_lose_uscore_on_death_percent from the player's uscore:
|
||||
@@ -963,9 +974,9 @@ void DestroyableComponent::DoHardcoreModeDrops(const LWOOBJID source){
|
||||
if (inventory) {
|
||||
//get the items inventory:
|
||||
auto items = inventory->GetInventory(eInventoryType::ITEMS);
|
||||
if (items){
|
||||
if (items) {
|
||||
auto itemMap = items->GetItems();
|
||||
if (!itemMap.empty()){
|
||||
if (!itemMap.empty()) {
|
||||
for (const auto& item : itemMap) {
|
||||
//drop the item:
|
||||
if (!item.second) continue;
|
||||
|
||||
@@ -24,6 +24,7 @@ public:
|
||||
DestroyableComponent(Entity* parentEntity);
|
||||
~DestroyableComponent() override;
|
||||
|
||||
void Update(float deltaTime) override;
|
||||
void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate) override;
|
||||
void LoadFromXml(tinyxml2::XMLDocument* doc) override;
|
||||
void UpdateXml(tinyxml2::XMLDocument* doc) override;
|
||||
@@ -166,6 +167,11 @@ public:
|
||||
*/
|
||||
bool IsImmune() const;
|
||||
|
||||
/**
|
||||
* @return whether this entity is currently immune to attacks due to a damage cooldown period
|
||||
*/
|
||||
bool IsCooldownImmune() const;
|
||||
|
||||
/**
|
||||
* Sets if this entity has GM immunity, making it not killable
|
||||
* @param value the GM immunity of this entity
|
||||
@@ -406,18 +412,23 @@ public:
|
||||
);
|
||||
|
||||
// Getters for status immunities
|
||||
const bool GetImmuneToBasicAttack() {return m_ImmuneToBasicAttackCount > 0;};
|
||||
const bool GetImmuneToDamageOverTime() {return m_ImmuneToDamageOverTimeCount > 0;};
|
||||
const bool GetImmuneToKnockback() {return m_ImmuneToKnockbackCount > 0;};
|
||||
const bool GetImmuneToInterrupt() {return m_ImmuneToInterruptCount > 0;};
|
||||
const bool GetImmuneToSpeed() {return m_ImmuneToSpeedCount > 0;};
|
||||
const bool GetImmuneToImaginationGain() {return m_ImmuneToImaginationGainCount > 0;};
|
||||
const bool GetImmuneToImaginationLoss() {return m_ImmuneToImaginationLossCount > 0;};
|
||||
const bool GetImmuneToQuickbuildInterrupt() {return m_ImmuneToQuickbuildInterruptCount > 0;};
|
||||
const bool GetImmuneToPullToPoint() {return m_ImmuneToPullToPointCount > 0;};
|
||||
const bool GetImmuneToBasicAttack() { return m_ImmuneToBasicAttackCount > 0; };
|
||||
const bool GetImmuneToDamageOverTime() { return m_ImmuneToDamageOverTimeCount > 0; };
|
||||
const bool GetImmuneToKnockback() { return m_ImmuneToKnockbackCount > 0; };
|
||||
const bool GetImmuneToInterrupt() { return m_ImmuneToInterruptCount > 0; };
|
||||
const bool GetImmuneToSpeed() { return m_ImmuneToSpeedCount > 0; };
|
||||
const bool GetImmuneToImaginationGain() { return m_ImmuneToImaginationGainCount > 0; };
|
||||
const bool GetImmuneToImaginationLoss() { return m_ImmuneToImaginationLossCount > 0; };
|
||||
const bool GetImmuneToQuickbuildInterrupt() { return m_ImmuneToQuickbuildInterruptCount > 0; };
|
||||
const bool GetImmuneToPullToPoint() { return m_ImmuneToPullToPointCount > 0; };
|
||||
|
||||
int32_t GetDeathBehavior() const { return m_DeathBehavior; }
|
||||
// Damage cooldown setters/getters
|
||||
void SetDamageCooldownTimer(float value) { m_DamageCooldownTimer = value; }
|
||||
float GetDamageCooldownTimer() { return m_DamageCooldownTimer; }
|
||||
|
||||
// Death behavior setters/getters
|
||||
void SetDeathBehavior(int32_t value) { m_DeathBehavior = value; }
|
||||
int32_t GetDeathBehavior() const { return m_DeathBehavior; }
|
||||
|
||||
/**
|
||||
* Utility to reset all stats to the default stats based on items and completed missions
|
||||
@@ -605,6 +616,11 @@ private:
|
||||
* Death behavior type. If 0, the client plays a death animation as opposed to a smash animation.
|
||||
*/
|
||||
int32_t m_DeathBehavior;
|
||||
|
||||
/**
|
||||
* Damage immunity cooldown timer. Set to a value that then counts down to create a damage cooldown for players
|
||||
*/
|
||||
float m_DamageCooldownTimer;
|
||||
};
|
||||
|
||||
#endif // DESTROYABLECOMPONENT_H
|
||||
|
||||
@@ -300,38 +300,26 @@ void InventoryComponent::AddItem(
|
||||
}
|
||||
}
|
||||
|
||||
void InventoryComponent::RemoveItem(const LOT lot, const uint32_t count, eInventoryType inventoryType, const bool ignoreBound) {
|
||||
bool InventoryComponent::RemoveItem(const LOT lot, const uint32_t count, eInventoryType inventoryType, const bool ignoreBound, const bool silent) {
|
||||
if (count == 0) {
|
||||
LOG("Attempted to remove 0 of item (%i) from the inventory!", lot);
|
||||
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (inventoryType == INVALID) {
|
||||
inventoryType = Inventory::FindInventoryTypeForLot(lot);
|
||||
}
|
||||
|
||||
if (inventoryType == INVALID) inventoryType = Inventory::FindInventoryTypeForLot(lot);
|
||||
auto* inventory = GetInventory(inventoryType);
|
||||
|
||||
if (inventory == nullptr) {
|
||||
return;
|
||||
}
|
||||
if (!inventory) return false;
|
||||
|
||||
auto left = std::min<uint32_t>(count, inventory->GetLotCount(lot));
|
||||
if (left != count) return false;
|
||||
|
||||
while (left > 0) {
|
||||
auto* item = FindItemByLot(lot, inventoryType, false, ignoreBound);
|
||||
|
||||
if (item == nullptr) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (!item) break;
|
||||
const auto delta = std::min<uint32_t>(left, item->GetCount());
|
||||
|
||||
item->SetCount(item->GetCount() - delta);
|
||||
|
||||
item->SetCount(item->GetCount() - delta, silent);
|
||||
left -= delta;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void InventoryComponent::MoveItemToInventory(Item* item, const eInventoryType inventory, const uint32_t count, const bool showFlyingLot, bool isModMoveAndEquip, const bool ignoreEquipped, const int32_t preferredSlot) {
|
||||
|
||||
@@ -118,8 +118,9 @@ public:
|
||||
* @param count the number of items to remove
|
||||
* @param inventoryType optional inventory type to remove the item from
|
||||
* @param ignoreBound ignores bound items
|
||||
* @param silent silently remove the item
|
||||
*/
|
||||
void RemoveItem(LOT lot, uint32_t count, eInventoryType inventoryType = INVALID, bool ignoreBound = false);
|
||||
bool RemoveItem(LOT lot, uint32_t count, eInventoryType inventoryType = INVALID, bool ignoreBound = false, bool silent = false);
|
||||
|
||||
/**
|
||||
* Moves an existing item to an inventory of the entity
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
#include "RenderComponent.h"
|
||||
#include "eObjectBits.h"
|
||||
#include "eGameMasterLevel.h"
|
||||
#include "eMissionState.h"
|
||||
|
||||
std::unordered_map<LOT, PetComponent::PetPuzzleData> PetComponent::buildCache{};
|
||||
std::unordered_map<LWOOBJID, LWOOBJID> PetComponent::currentActivities{};
|
||||
@@ -439,9 +440,15 @@ void PetComponent::Update(float deltaTime) {
|
||||
}
|
||||
}
|
||||
|
||||
auto* missionComponent = owner->GetComponent<MissionComponent>();
|
||||
if (!missionComponent) return;
|
||||
|
||||
// Determine if the "Lost Tags" mission has been completed and digging has been unlocked
|
||||
const bool digUnlocked = missionComponent->GetMissionState(842) == eMissionState::COMPLETE;
|
||||
|
||||
Entity* closestTresure = PetDigServer::GetClosestTresure(position);
|
||||
|
||||
if (closestTresure != nullptr) {
|
||||
if (closestTresure != nullptr && digUnlocked) {
|
||||
// Skeleton Dragon Pat special case for bone digging
|
||||
if (closestTresure->GetLOT() == 12192 && m_Parent->GetLOT() != 13067) {
|
||||
goto skipTresure;
|
||||
|
||||
@@ -423,6 +423,25 @@ void PropertyManagementComponent::DeleteModel(const LWOOBJID id, const int delet
|
||||
return;
|
||||
}
|
||||
|
||||
auto* model = Game::entityManager->GetEntity(id);
|
||||
|
||||
if (model == nullptr) {
|
||||
LOG("Failed to find model entity");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (model->GetLOT() == 14 && deleteReason == 0) {
|
||||
LOG("User is trying to pick up a BBB model, but this is not implemented, so we return to prevent the user from losing the model");
|
||||
|
||||
GameMessages::SendUGCEquipPostDeleteBasedOnEditMode(entity->GetObjectID(), entity->GetSystemAddress(), LWOOBJID_EMPTY, 0);
|
||||
|
||||
// Need this to pop the user out of their current state
|
||||
GameMessages::SendPlaceModelResponse(entity->GetObjectID(), entity->GetSystemAddress(), entity->GetPosition(), m_Parent->GetObjectID(), 14, entity->GetRotation());
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const auto index = models.find(id);
|
||||
|
||||
if (index == models.end()) {
|
||||
@@ -441,14 +460,6 @@ void PropertyManagementComponent::DeleteModel(const LWOOBJID id, const int delet
|
||||
LOG("Failed to find spawner");
|
||||
}
|
||||
|
||||
auto* model = Game::entityManager->GetEntity(id);
|
||||
|
||||
if (model == nullptr) {
|
||||
LOG("Failed to find model entity");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
Game::entityManager->DestructEntity(model);
|
||||
|
||||
LOG("Deleting model LOT %i", model->GetLOT());
|
||||
|
||||
@@ -2708,7 +2708,7 @@ void GameMessages::HandleBBBSaveRequest(RakNet::BitStream* inStream, Entity* ent
|
||||
PropertyManagementComponent::Instance()->AddModel(newEntity->GetObjectID(), newIDL);
|
||||
}
|
||||
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
void GameMessages::HandlePropertyEntranceSync(RakNet::BitStream* inStream, Entity* entity, const SystemAddress& sysAddr) {
|
||||
@@ -5119,6 +5119,10 @@ void GameMessages::HandleModularBuildConvertModel(RakNet::BitStream* inStream, E
|
||||
|
||||
item->Disassemble(TEMP_MODELS);
|
||||
|
||||
std::unique_ptr<sql::PreparedStatement> stmt(Database::CreatePreppedStmt("DELETE FROM ugc_modular_build where ugc_id = ?"));
|
||||
stmt->setUInt64(1, item->GetSubKey());
|
||||
stmt->execute();
|
||||
|
||||
item->SetCount(item->GetCount() - 1, false, false, true, eLootSourceType::QUICKBUILD);
|
||||
}
|
||||
|
||||
@@ -5441,10 +5445,8 @@ void GameMessages::HandleRemoveItemFromInventory(RakNet::BitStream* inStream, En
|
||||
iStackCount = std::min<uint32_t>(item->GetCount(), iStackCount);
|
||||
|
||||
if (bConfirmed) {
|
||||
for (auto i = 0; i < iStackCount; ++i) {
|
||||
if (eInvType == eInventoryType::MODELS) {
|
||||
item->DisassembleModel();
|
||||
}
|
||||
if (eInvType == eInventoryType::MODELS) {
|
||||
item->DisassembleModel(iStackCount);
|
||||
}
|
||||
|
||||
item->SetCount(item->GetCount() - iStackCount, true);
|
||||
@@ -5564,13 +5566,6 @@ void GameMessages::HandleModularBuildFinish(RakNet::BitStream* inStream, Entity*
|
||||
InventoryComponent* inv = static_cast<InventoryComponent*>(character->GetComponent(eReplicaComponentType::INVENTORY));
|
||||
if (!inv) return;
|
||||
|
||||
LOG("Build finished");
|
||||
|
||||
GameMessages::SendFinishArrangingWithItem(character, entity->GetObjectID()); // kick them from modular build
|
||||
GameMessages::SendModularBuildEnd(character); // i dont know if this does anything but DLUv2 did it
|
||||
|
||||
//inv->UnequipItem(inv->GetItemStackByLOT(6086, eInventoryType::ITEMS)); // take off the thinking cap
|
||||
//Game::entityManager->SerializeEntity(entity);
|
||||
|
||||
uint8_t count; // 3 for rockets, 7 for cars
|
||||
|
||||
@@ -5605,50 +5600,61 @@ void GameMessages::HandleModularBuildFinish(RakNet::BitStream* inStream, Entity*
|
||||
}
|
||||
}
|
||||
|
||||
const auto moduleAssembly = new LDFData<std::u16string>(u"assemblyPartLOTs", modules);
|
||||
ObjectIDManager::Instance()->RequestPersistentID([=](uint32_t newId) {
|
||||
LOG("Build finished");
|
||||
GameMessages::SendFinishArrangingWithItem(character, entity->GetObjectID()); // kick them from modular build
|
||||
GameMessages::SendModularBuildEnd(character); // i dont know if this does anything but DLUv2 did it
|
||||
|
||||
std::vector<LDFBaseData*> config;
|
||||
config.push_back(moduleAssembly);
|
||||
//inv->UnequipItem(inv->GetItemStackByLOT(6086, eInventoryType::ITEMS)); // take off the thinking cap
|
||||
//Game::entityManager->SerializeEntity(entity);
|
||||
|
||||
LWOOBJID newIdBig;
|
||||
// Make sure a subkey isnt already in use. Persistent Ids do not make sense here since this only needs to be unique for
|
||||
// this character. Because of that, we just generate a random id and check for a collision.
|
||||
do {
|
||||
newIdBig = ObjectIDManager::Instance()->GenerateRandomObjectID();
|
||||
const auto moduleAssembly = new LDFData<std::u16string>(u"assemblyPartLOTs", modules);
|
||||
|
||||
std::vector<LDFBaseData*> config;
|
||||
config.push_back(moduleAssembly);
|
||||
|
||||
LWOOBJID newIdBig = newId;
|
||||
GeneralUtils::SetBit(newIdBig, eObjectBits::CHARACTER);
|
||||
} while (inv->FindItemBySubKey(newIdBig));
|
||||
|
||||
if (count == 3) {
|
||||
inv->AddItem(6416, 1, eLootSourceType::QUICKBUILD, eInventoryType::MODELS, config, LWOOBJID_EMPTY, true, false, newIdBig);
|
||||
} else if (count == 7) {
|
||||
inv->AddItem(8092, 1, eLootSourceType::QUICKBUILD, eInventoryType::MODELS, config, LWOOBJID_EMPTY, true, false, newIdBig);
|
||||
}
|
||||
|
||||
auto* missionComponent = character->GetComponent<MissionComponent>();
|
||||
|
||||
if (entity->GetLOT() != 9980 || Game::server->GetZoneID() != 1200) {
|
||||
if (missionComponent != nullptr) {
|
||||
missionComponent->Progress(eMissionTaskType::SCRIPT, entity->GetLOT(), entity->GetObjectID());
|
||||
if (count >= 7 && everyPieceSwapped) missionComponent->Progress(eMissionTaskType::RACING, LWOOBJID_EMPTY, (LWOOBJID)eRacingTaskParam::MODULAR_BUILDING);
|
||||
if (count == 3) {
|
||||
inv->AddItem(6416, 1, eLootSourceType::QUICKBUILD, eInventoryType::MODELS, config, LWOOBJID_EMPTY, true, false, newIdBig);
|
||||
} else if (count == 7) {
|
||||
inv->AddItem(8092, 1, eLootSourceType::QUICKBUILD, eInventoryType::MODELS, config, LWOOBJID_EMPTY, true, false, newIdBig);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ScriptComponent* script = static_cast<ScriptComponent*>(entity->GetComponent(eReplicaComponentType::SCRIPT));
|
||||
std::unique_ptr<sql::PreparedStatement> stmt(Database::CreatePreppedStmt("INSERT INTO ugc_modular_build (ugc_id, ldf_config, character_id) VALUES (?,?,?)"));
|
||||
stmt->setUInt64(1, newIdBig);
|
||||
stmt->setString(2, GeneralUtils::UTF16ToWTF8(modules));
|
||||
auto* pCharacter = character->GetCharacter();
|
||||
pCharacter ? stmt->setUInt(3, pCharacter->GetID()) : stmt->setNull(3, sql::DataType::BIGINT);
|
||||
stmt->execute();
|
||||
|
||||
for (CppScripts::Script* script : CppScripts::GetEntityScripts(entity)) {
|
||||
script->OnModularBuildExit(entity, character, count >= 3, modList);
|
||||
}
|
||||
auto* missionComponent = character->GetComponent<MissionComponent>();
|
||||
|
||||
// Move remaining temp models back to models
|
||||
std::vector<Item*> items;
|
||||
if (entity->GetLOT() != 9980 || Game::server->GetZoneID() != 1200) {
|
||||
if (missionComponent != nullptr) {
|
||||
missionComponent->Progress(eMissionTaskType::SCRIPT, entity->GetLOT(), entity->GetObjectID());
|
||||
if (count >= 7 && everyPieceSwapped) missionComponent->Progress(eMissionTaskType::RACING, LWOOBJID_EMPTY, (LWOOBJID)eRacingTaskParam::MODULAR_BUILDING);
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto& pair : temp->GetItems()) {
|
||||
items.push_back(pair.second);
|
||||
}
|
||||
ScriptComponent* script = static_cast<ScriptComponent*>(entity->GetComponent(eReplicaComponentType::SCRIPT));
|
||||
|
||||
for (auto* item : items) {
|
||||
inv->MoveItemToInventory(item, eInventoryType::MODELS, item->GetCount(), false);
|
||||
for (CppScripts::Script* script : CppScripts::GetEntityScripts(entity)) {
|
||||
script->OnModularBuildExit(entity, character, count >= 3, modList);
|
||||
}
|
||||
|
||||
// Move remaining temp models back to models
|
||||
std::vector<Item*> items;
|
||||
|
||||
for (const auto& pair : temp->GetItems()) {
|
||||
items.push_back(pair.second);
|
||||
}
|
||||
|
||||
for (auto* item : items) {
|
||||
inv->MoveItemToInventory(item, eInventoryType::MODELS, item->GetCount(), false);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -19,6 +19,8 @@
|
||||
#include "eObjectBits.h"
|
||||
#include "eReplicaComponentType.h"
|
||||
#include "eUseItemResponse.h"
|
||||
#include "dZoneManager.h"
|
||||
#include "ChatPackets.h"
|
||||
|
||||
#include "CDBrickIDTableTable.h"
|
||||
#include "CDObjectSkillsTable.h"
|
||||
@@ -292,12 +294,19 @@ void Item::UseNonEquip(Item* item) {
|
||||
|
||||
const auto type = static_cast<eItemType>(info->itemType);
|
||||
if (type == eItemType::MOUNT) {
|
||||
playerInventoryComponent->HandlePossession(this);
|
||||
// TODO Check if mounts are allowed to be spawned
|
||||
} else if (type == eItemType::PET_INVENTORY_ITEM && subKey != LWOOBJID_EMPTY) {
|
||||
const auto& databasePet = playerInventoryComponent->GetDatabasePet(subKey);
|
||||
if (databasePet.lot != LOT_NULL) {
|
||||
playerInventoryComponent->SpawnPet(this);
|
||||
if (Game::zoneManager->GetMountsAllowed()){
|
||||
playerInventoryComponent->HandlePossession(this);
|
||||
} else {
|
||||
ChatPackets::SendSystemMessage(playerEntity->GetSystemAddress(), u"Mounts are not allowed in this zone");
|
||||
}
|
||||
} else if (type == eItemType::PET_INVENTORY_ITEM && subKey != LWOOBJID_EMPTY ) {
|
||||
if (Game::zoneManager->GetPetsAllowed()){
|
||||
const auto& databasePet = playerInventoryComponent->GetDatabasePet(subKey);
|
||||
if (databasePet.lot != LOT_NULL) {
|
||||
playerInventoryComponent->SpawnPet(this);
|
||||
}
|
||||
} else {
|
||||
ChatPackets::SendSystemMessage(playerEntity->GetSystemAddress(), u"Pets are not allowed in this zone");
|
||||
}
|
||||
// This precondition response is taken care of in SpawnPet().
|
||||
} else {
|
||||
@@ -386,14 +395,13 @@ void Item::Disassemble(const eInventoryType inventoryType) {
|
||||
}
|
||||
}
|
||||
|
||||
void Item::DisassembleModel() {
|
||||
void Item::DisassembleModel(uint32_t numToDismantle) {
|
||||
auto* table = CDClientManager::Instance().GetTable<CDComponentsRegistryTable>();
|
||||
|
||||
const auto componentId = table->GetByIDAndType(GetLot(), eReplicaComponentType::RENDER);
|
||||
|
||||
auto query = CDClientDatabase::CreatePreppedStmt(
|
||||
"SELECT render_asset, LXFMLFolder FROM RenderComponent WHERE id = ?;");
|
||||
query.bind(1, (int)componentId);
|
||||
auto query = CDClientDatabase::CreatePreppedStmt("SELECT render_asset, LXFMLFolder FROM RenderComponent WHERE id = ?;");
|
||||
query.bind(1, static_cast<int>(componentId));
|
||||
|
||||
auto result = query.execQuery();
|
||||
|
||||
@@ -417,8 +425,6 @@ void Item::DisassembleModel() {
|
||||
|
||||
std::istream file(&buffer);
|
||||
|
||||
result.finalize();
|
||||
|
||||
if (!file.good()) {
|
||||
buffer.close();
|
||||
return;
|
||||
@@ -429,39 +435,49 @@ void Item::DisassembleModel() {
|
||||
|
||||
buffer.close();
|
||||
|
||||
if (data.str().empty()) {
|
||||
uint32_t fileSize;
|
||||
file.seekg(0, std::ios::end);
|
||||
fileSize = static_cast<uint32_t>(file.tellg());
|
||||
file.seekg(0, std::ios::beg);
|
||||
|
||||
if (fileSize == 0) return;
|
||||
|
||||
tinyxml2::XMLDocument doc;
|
||||
|
||||
if (doc.Parse(data.str().c_str(), data.str().size()) != tinyxml2::XML_SUCCESS) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto* doc = new tinyxml2::XMLDocument();
|
||||
|
||||
if (!doc) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (doc->Parse(data.str().c_str(), data.str().size()) != 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<int> parts;
|
||||
|
||||
auto* lxfml = doc->FirstChildElement("LXFML");
|
||||
auto* lxfml = doc.FirstChildElement("LXFML");
|
||||
if (!lxfml) return;
|
||||
auto* bricks = lxfml->FirstChildElement("Bricks");
|
||||
std::string searchTerm = "Brick";
|
||||
|
||||
if (!bricks) {
|
||||
searchTerm = "Part";
|
||||
bricks = lxfml->FirstChildElement("Scene")->FirstChildElement("Model")->FirstChildElement("Group");
|
||||
auto* scene = lxfml->FirstChildElement("Scene");
|
||||
if (!scene) return;
|
||||
|
||||
if (!bricks) {
|
||||
return;
|
||||
}
|
||||
auto* model = scene->FirstChildElement("Model");
|
||||
if (!model) return;
|
||||
|
||||
auto* group = model->FirstChildElement("Group");
|
||||
if (!group) return;
|
||||
}
|
||||
|
||||
auto* currentBrick = bricks->FirstChildElement(searchTerm.c_str());
|
||||
|
||||
// First iteration gets the count
|
||||
std::map<int32_t, int32_t> parts;
|
||||
while (currentBrick) {
|
||||
if (currentBrick->Attribute("designID") != nullptr) {
|
||||
parts.push_back(std::stoi(currentBrick->Attribute("designID")));
|
||||
auto* designID = currentBrick->Attribute("designID");
|
||||
if (designID) {
|
||||
uint32_t designId;
|
||||
if (!GeneralUtils::TryParse(designID, designId)) {
|
||||
LOG("Failed to parse designID %s", designID);
|
||||
continue;
|
||||
}
|
||||
parts[designId]++;
|
||||
}
|
||||
|
||||
currentBrick = currentBrick->NextSiblingElement(searchTerm.c_str());
|
||||
@@ -469,16 +485,16 @@ void Item::DisassembleModel() {
|
||||
|
||||
auto* brickIDTable = CDClientManager::Instance().GetTable<CDBrickIDTableTable>();
|
||||
|
||||
for (unsigned int part : parts) {
|
||||
const auto brickID = brickIDTable->Query([=](const CDBrickIDTable& entry) {
|
||||
return entry.LEGOBrickID == part;
|
||||
// Second iteration actually distributes the bricks
|
||||
for (const auto&[part, count] : parts) {
|
||||
const auto partLocal = part;
|
||||
const auto brickID = brickIDTable->Query([&](const CDBrickIDTable& entry) {
|
||||
return entry.LEGOBrickID == partLocal;
|
||||
});
|
||||
|
||||
if (brickID.empty()) {
|
||||
continue;
|
||||
}
|
||||
if (brickID.empty()) continue;
|
||||
|
||||
GetInventory()->GetComponent()->AddItem(brickID[0].NDObjectID, 1, eLootSourceType::DELETION);
|
||||
GetInventory()->GetComponent()->AddItem(brickID[0].NDObjectID, count * numToDismantle, eLootSourceType::DELETION);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -207,7 +207,7 @@ public:
|
||||
/**
|
||||
* Disassembles this item into bricks
|
||||
*/
|
||||
void DisassembleModel();
|
||||
void DisassembleModel(uint32_t numToDismantle);
|
||||
|
||||
/**
|
||||
* Removes the item from the linked inventory
|
||||
|
||||
@@ -247,7 +247,7 @@ void SlashCommandHandler::HandleChatCommand(const std::u16string& command, Entit
|
||||
}
|
||||
|
||||
if (chatCommand == "credits" || chatCommand == "info") {
|
||||
const auto& customText = chatCommand == "credits" ? VanityUtilities::ParseMarkdown((BinaryPathFinder::GetBinaryDir() / "vanity/CREDITS.md").string()) : VanityUtilities::ParseMarkdown((BinaryPathFinder::GetBinaryDir() / "vanity/INFO.md").string());
|
||||
const auto& customText = chatCommand == "credits" ? VanityUtilities::ParseMarkdown((BinaryPathFinder::GetBinaryDir() / "vanity/CREDITS.md").string()) : VanityUtilities::ParseMarkdown((BinaryPathFinder::GetBinaryDir() / "vanity/INFO.md").string());
|
||||
|
||||
{
|
||||
AMFArrayValue args;
|
||||
@@ -448,9 +448,8 @@ void SlashCommandHandler::HandleChatCommand(const std::u16string& command, Entit
|
||||
if (chatCommand == "kill" && args.size() == 1 && entity->GetGMLevel() >= eGameMasterLevel::DEVELOPER) {
|
||||
ChatPackets::SendSystemMessage(sysAddr, u"Brutally murdering that player, if online on this server.");
|
||||
|
||||
auto* user = UserManager::Instance()->GetUser(args[0]);
|
||||
if (user) {
|
||||
auto* player = Game::entityManager->GetEntity(user->GetLoggedInChar());
|
||||
auto* player = Player::GetPlayer(args[0]);
|
||||
if (player) {
|
||||
player->Smash(entity->GetObjectID());
|
||||
ChatPackets::SendSystemMessage(sysAddr, u"It has been done, do you feel good about yourself now?");
|
||||
return;
|
||||
@@ -1042,7 +1041,7 @@ void SlashCommandHandler::HandleChatCommand(const std::u16string& command, Entit
|
||||
}
|
||||
} else {
|
||||
accountId = player->GetParentUser()->GetAccountID();
|
||||
characterId = player->GetCharacter()->GetID();
|
||||
characterId = player->GetObjectID();
|
||||
}
|
||||
|
||||
auto* userUpdate = Database::CreatePreppedStmt("UPDATE accounts SET mute_expire = ? WHERE id = ?;");
|
||||
@@ -1490,6 +1489,24 @@ void SlashCommandHandler::HandleChatCommand(const std::u16string& command, Entit
|
||||
return;
|
||||
}
|
||||
|
||||
//Testing basic attack immunity
|
||||
if (chatCommand == "attackimmune" && args.size() >= 1 && entity->GetGMLevel() >= eGameMasterLevel::DEVELOPER) {
|
||||
auto* destroyableComponent = entity->GetComponent<DestroyableComponent>();
|
||||
|
||||
int32_t state = false;
|
||||
|
||||
if (!GeneralUtils::TryParse(args[0], state)) {
|
||||
ChatPackets::SendSystemMessage(sysAddr, u"Invalid state.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (destroyableComponent != nullptr) {
|
||||
destroyableComponent->SetIsImmune(state);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (chatCommand == "buff" && args.size() >= 2 && entity->GetGMLevel() >= eGameMasterLevel::DEVELOPER) {
|
||||
auto* buffComponent = entity->GetComponent<BuffComponent>();
|
||||
|
||||
@@ -1843,7 +1860,7 @@ void SlashCommandHandler::HandleChatCommand(const std::u16string& command, Entit
|
||||
|
||||
if (chatCommand == "castskill" && entity->GetGMLevel() >= eGameMasterLevel::DEVELOPER && args.size() >= 1) {
|
||||
auto* skillComponent = entity->GetComponent<SkillComponent>();
|
||||
if (skillComponent){
|
||||
if (skillComponent) {
|
||||
uint32_t skillId;
|
||||
|
||||
if (!GeneralUtils::TryParse(args[0], skillId)) {
|
||||
@@ -1860,7 +1877,7 @@ void SlashCommandHandler::HandleChatCommand(const std::u16string& command, Entit
|
||||
uint32_t skillId;
|
||||
int slot;
|
||||
auto* inventoryComponent = entity->GetComponent<InventoryComponent>();
|
||||
if (inventoryComponent){
|
||||
if (inventoryComponent) {
|
||||
if (!GeneralUtils::TryParse(args[0], slot)) {
|
||||
ChatPackets::SendSystemMessage(sysAddr, u"Error getting slot.");
|
||||
return;
|
||||
@@ -1869,7 +1886,7 @@ void SlashCommandHandler::HandleChatCommand(const std::u16string& command, Entit
|
||||
ChatPackets::SendSystemMessage(sysAddr, u"Error getting skill.");
|
||||
return;
|
||||
} else {
|
||||
if(inventoryComponent->SetSkill(slot, skillId)) ChatPackets::SendSystemMessage(sysAddr, u"Set skill to slot successfully");
|
||||
if (inventoryComponent->SetSkill(slot, skillId)) ChatPackets::SendSystemMessage(sysAddr, u"Set skill to slot successfully");
|
||||
else ChatPackets::SendSystemMessage(sysAddr, u"Set skill to slot failed");
|
||||
}
|
||||
}
|
||||
@@ -1878,7 +1895,7 @@ void SlashCommandHandler::HandleChatCommand(const std::u16string& command, Entit
|
||||
|
||||
if (chatCommand == "setfaction" && entity->GetGMLevel() >= eGameMasterLevel::DEVELOPER && args.size() >= 1) {
|
||||
auto* destroyableComponent = entity->GetComponent<DestroyableComponent>();
|
||||
if (destroyableComponent){
|
||||
if (destroyableComponent) {
|
||||
int32_t faction;
|
||||
|
||||
if (!GeneralUtils::TryParse(args[0], faction)) {
|
||||
@@ -1893,7 +1910,7 @@ void SlashCommandHandler::HandleChatCommand(const std::u16string& command, Entit
|
||||
|
||||
if (chatCommand == "addfaction" && entity->GetGMLevel() >= eGameMasterLevel::DEVELOPER && args.size() >= 1) {
|
||||
auto* destroyableComponent = entity->GetComponent<DestroyableComponent>();
|
||||
if (destroyableComponent){
|
||||
if (destroyableComponent) {
|
||||
int32_t faction;
|
||||
|
||||
if (!GeneralUtils::TryParse(args[0], faction)) {
|
||||
@@ -1908,7 +1925,7 @@ void SlashCommandHandler::HandleChatCommand(const std::u16string& command, Entit
|
||||
|
||||
if (chatCommand == "getfactions" && entity->GetGMLevel() >= eGameMasterLevel::DEVELOPER) {
|
||||
auto* destroyableComponent = entity->GetComponent<DestroyableComponent>();
|
||||
if (destroyableComponent){
|
||||
if (destroyableComponent) {
|
||||
ChatPackets::SendSystemMessage(sysAddr, u"Friendly factions:");
|
||||
for (const auto entry : destroyableComponent->GetFactionIDs()) {
|
||||
ChatPackets::SendSystemMessage(sysAddr, (GeneralUtils::to_u16string(entry)));
|
||||
|
||||
@@ -34,6 +34,7 @@
|
||||
#include "eGameMasterLevel.h"
|
||||
#include "eReplicaComponentType.h"
|
||||
#include "CheatDetection.h"
|
||||
#include "Amf3.h"
|
||||
|
||||
void ClientPackets::HandleChatMessage(const SystemAddress& sysAddr, Packet* packet) {
|
||||
User* user = UserManager::Instance()->GetUser(sysAddr);
|
||||
@@ -402,3 +403,40 @@ void ClientPackets::HandleChatModerationRequest(const SystemAddress& sysAddr, Pa
|
||||
user->SetLastChatMessageApproved(bAllClean);
|
||||
WorldPackets::SendChatModerationResponse(sysAddr, bAllClean, requestID, receiver, segments);
|
||||
}
|
||||
|
||||
void ClientPackets::SendTop5HelpIssues(Packet* packet) {
|
||||
auto* user = UserManager::Instance()->GetUser(packet->systemAddress);
|
||||
if (!user) return;
|
||||
auto* character = user->GetLastUsedChar();
|
||||
if (!character) return;
|
||||
auto * entity = character->GetEntity();
|
||||
if (!entity) return;
|
||||
|
||||
CINSTREAM_SKIP_HEADER;
|
||||
int32_t language = 0;
|
||||
inStream.Read(language);
|
||||
|
||||
// TODO: Handle different languages in a nice way
|
||||
// 0: en_US
|
||||
// 1: pl_US
|
||||
// 2: de_DE
|
||||
// 3: en_GB
|
||||
|
||||
AMFArrayValue data;
|
||||
// Summaries
|
||||
data.Insert("Summary0", Game::config->GetValue("help_0_summary"));
|
||||
data.Insert("Summary1", Game::config->GetValue("help_1_summary"));
|
||||
data.Insert("Summary2", Game::config->GetValue("help_2_summary"));
|
||||
data.Insert("Summary3", Game::config->GetValue("help_3_summary"));
|
||||
data.Insert("Summary4", Game::config->GetValue("help_4_summary"));
|
||||
|
||||
// Descriptions
|
||||
data.Insert("Description0", Game::config->GetValue("help_0_description"));
|
||||
data.Insert("Description1", Game::config->GetValue("help_1_description"));
|
||||
data.Insert("Description2", Game::config->GetValue("help_2_description"));
|
||||
data.Insert("Description3", Game::config->GetValue("help_3_description"));
|
||||
data.Insert("Description4", Game::config->GetValue("help_4_description"));
|
||||
|
||||
GameMessages::SendUIMessageServerToSingleClient(entity, packet->systemAddress, "UIHelpTop5", data);
|
||||
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ namespace ClientPackets {
|
||||
void HandleChatMessage(const SystemAddress& sysAddr, Packet* packet);
|
||||
void HandleClientPositionUpdate(const SystemAddress& sysAddr, Packet* packet);
|
||||
void HandleChatModerationRequest(const SystemAddress& sysAddr, Packet* packet);
|
||||
void SendTop5HelpIssues(Packet* packet);
|
||||
};
|
||||
|
||||
#endif // CLIENTPACKETS_H
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "PerformanceManager.h"
|
||||
|
||||
#include "CDZoneTableTable.h"
|
||||
#include "CDClientManager.h"
|
||||
#include "UserManager.h"
|
||||
|
||||
#define SOCIAL { lowFrameDelta }
|
||||
@@ -68,11 +69,30 @@ std::map<LWOMAPID, PerformanceProfile> PerformanceManager::m_Profiles = {
|
||||
};
|
||||
|
||||
void PerformanceManager::SelectProfile(LWOMAPID mapID) {
|
||||
const auto pair = m_Profiles.find(mapID);
|
||||
// Try to get it from zoneTable
|
||||
CDZoneTableTable* zoneTable = CDClientManager::Instance().GetTable<CDZoneTableTable>();
|
||||
if (zoneTable) {
|
||||
const CDZoneTable* zone = zoneTable->Query(mapID);
|
||||
if (zone) {
|
||||
if (zone->serverPhysicsFramerate == "high"){
|
||||
m_CurrentProfile = { highFrameDelta };
|
||||
return;
|
||||
}
|
||||
if (zone->serverPhysicsFramerate == "medium"){
|
||||
m_CurrentProfile = { mediumFrameDelta };
|
||||
return;
|
||||
}
|
||||
if (zone->serverPhysicsFramerate == "low"){
|
||||
m_CurrentProfile = { lowFrameDelta };
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Fall back to hardcoded list and defaults
|
||||
const auto pair = m_Profiles.find(mapID);
|
||||
if (pair == m_Profiles.end()) {
|
||||
m_CurrentProfile = m_DefaultProfile;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -1297,6 +1297,12 @@ void HandlePacket(Packet* packet) {
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
case eWorldMessageType::UI_HELP_TOP_5: {
|
||||
ClientPackets::SendTop5HelpIssues(packet);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
LOG("Unknown world packet received: %i", int(packet->data[3]));
|
||||
}
|
||||
|
||||
@@ -40,6 +40,9 @@ void dZoneManager::Initialize(const LWOZONEID& zoneID) {
|
||||
Game::entityManager->SetGhostDistanceMax(max + min);
|
||||
Game::entityManager->SetGhostDistanceMin(max);
|
||||
m_PlayerLoseCoinsOnDeath = zone->PlayerLoseCoinsOnDeath;
|
||||
m_DisableSaveLocation = zone->disableSaveLoc;
|
||||
m_MountsAllowed = zone->mountsAllowed;
|
||||
m_PetsAllowed = zone->petsAllowed;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -41,6 +41,9 @@ public:
|
||||
void Update(float deltaTime);
|
||||
Entity* GetZoneControlObject() { return m_ZoneControlObject; }
|
||||
bool GetPlayerLoseCoinOnDeath() { return m_PlayerLoseCoinsOnDeath; }
|
||||
bool GetDisableSaveLocation() { return m_DisableSaveLocation; }
|
||||
bool GetMountsAllowed() { return m_MountsAllowed; }
|
||||
bool GetPetsAllowed() { return m_PetsAllowed; }
|
||||
uint32_t GetUniqueMissionIdStartingValue();
|
||||
bool CheckIfAccessibleZone(LWOMAPID zoneID);
|
||||
|
||||
@@ -58,7 +61,10 @@ private:
|
||||
|
||||
Zone* m_pZone = nullptr;
|
||||
LWOZONEID m_ZoneID;
|
||||
bool m_PlayerLoseCoinsOnDeath; //Do players drop coins in this zone when smashed
|
||||
bool m_PlayerLoseCoinsOnDeath = false;
|
||||
bool m_DisableSaveLocation = false;
|
||||
bool m_MountsAllowed = true;
|
||||
bool m_PetsAllowed = true;
|
||||
std::map<LWOOBJID, Spawner*> m_Spawners;
|
||||
WorldConfig* m_WorldConfig = nullptr;
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
|ban|`/ban <username>`|Bans a user from the server.|4|
|
||||
|approveproperty|`/approveproperty`|Approves the property the player is currently visiting.|5|
|
||||
|mute|`/mute <username> (days) (hours)`|Mute player for the given amount of time. If no time is given, the mute is indefinite.|6|
|
||||
|attackimmune|`/attackimmune <value>`|Sets the character's immunity to basic attacks state, where value can be one of "1", to make yourself immune to basic attack damage, or "0" to undo.|8|
|
||||
|gmimmune|`/gmimmunve <value>`|Sets the character's GMImmune state, where value can be one of "1", to make yourself immune to damage, or "0" to undo.|8|
|
||||
|gminvis|`/gminvis`|Toggles invisibility for the character, though it's currently a bit buggy. Requires nonzero GM Level for the character, but the account must have a GM level of 8.|8|
|
||||
|setname|`/setname <name>`|Sets a temporary name for your player. The name resets when you log out.|8|
|
||||
|
||||
5
migrations/dlu/12_modular_build_ugc.sql
Normal file
5
migrations/dlu/12_modular_build_ugc.sql
Normal file
@@ -0,0 +1,5 @@
|
||||
CREATE TABLE IF NOT EXISTS ugc_modular_build (
|
||||
ugc_id BIGINT NOT NULL PRIMARY KEY,
|
||||
character_id BIGINT NOT NULL REFERENCES charinfo(id) ON DELETE CASCADE,
|
||||
ldf_config VARCHAR(60) NOT NULL
|
||||
);
|
||||
@@ -1,2 +1,11 @@
|
||||
# Port number
|
||||
port=2005
|
||||
|
||||
# If you would like to increase the maximum number of best friends a player can have on the server
|
||||
# Change the value below to what you would like this to be (5 is live accurate)
|
||||
max_number_of_best_friends=5
|
||||
|
||||
# If you would like to increase the maximum number of friends a player can have on the server
|
||||
# Change the value below to what you would like this to be (50 is live accurate)
|
||||
# going over 50 will be allowed in some secnarios, but proper handling will require client modding
|
||||
max_number_of_friends=50
|
||||
|
||||
Binary file not shown.
@@ -40,10 +40,6 @@ classic_survival_scoring=0
|
||||
# If this value is 1, pets will consume imagination as they did in live. if 0 they will not consume imagination at all.
|
||||
pets_take_imagination=1
|
||||
|
||||
# If you would like to increase the maximum number of best friends a player can have on the server
|
||||
# Change the value below to what you would like this to be (5 is live accurate)
|
||||
max_number_of_best_friends=5
|
||||
|
||||
# Disables loot drops
|
||||
disable_drops=0
|
||||
|
||||
@@ -64,3 +60,15 @@ allow_nameplate_off=0
|
||||
|
||||
# Turn logging of IP addresses for anti-cheat reporting on (1) or off(0)
|
||||
log_ip_addresses_for_anti_cheat=1
|
||||
|
||||
help_0_summary=Got an issue?
|
||||
help_1_summary=Stuck loading?
|
||||
help_2_summary=Missing features?
|
||||
help_3_summary=Get smashed?
|
||||
help_4_summary=Want to contribute?
|
||||
|
||||
help_0_description=Go to the DarkflameServer repository on GitHub to view issues and discussions about the server emulator!<br/><br/><a href="https://github.com/DarkflameUniverse/DarkflameServer"><font color="#0000EE">Click Here to go there!</font></a><br/><br/>
|
||||
help_1_description=Try switching networks, using a VPN, or using your phone's hotspot to resolve the issue.<br/><br/>
|
||||
help_2_description=While DarkflameServer is a mostly complete emulator, there are still some features that aren't implemented. You can track these on the GitHub issues page.<br/><br/>
|
||||
help_3_description=Skill issue!<br/><br/>
|
||||
help_4_description=Visit Discussions on the DarkflameServer GitHub page<br/>to ask questions and collaborate with other devs!<br/><br/>
|
||||
|
||||
@@ -536,3 +536,22 @@ TEST_F(DestroyableTest, DestroyableComponentImmunityTest) {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the Damage cooldown timer of DestroyableComponent
|
||||
*/
|
||||
TEST_F(DestroyableTest, DestroyableComponentDamageCooldownTest) {
|
||||
// Test the damage immune timer state (anything above 0.0f)
|
||||
destroyableComponent->SetDamageCooldownTimer(1.0f);
|
||||
EXPECT_FLOAT_EQ(destroyableComponent->GetDamageCooldownTimer(), 1.0f);
|
||||
ASSERT_TRUE(destroyableComponent->IsCooldownImmune());
|
||||
|
||||
// Test that the Update() function correctly decrements the damage cooldown timer
|
||||
destroyableComponent->Update(0.5f);
|
||||
EXPECT_FLOAT_EQ(destroyableComponent->GetDamageCooldownTimer(), 0.5f);
|
||||
ASSERT_TRUE(destroyableComponent->IsCooldownImmune());
|
||||
|
||||
// Test the non damage immune timer state (anything below or equal to 0.0f)
|
||||
destroyableComponent->SetDamageCooldownTimer(0.0f);
|
||||
EXPECT_FLOAT_EQ(destroyableComponent->GetDamageCooldownTimer(), 0.0f);
|
||||
ASSERT_FALSE(destroyableComponent->IsCooldownImmune());
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user