diff --git a/CMakeLists.txt b/CMakeLists.txt index cf7988c8..fe1f2751 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -123,6 +123,9 @@ make_directory(${CMAKE_BINARY_DIR}/res) # Create a /locale directory make_directory(${CMAKE_BINARY_DIR}/locale) +# Create a /logs directory +make_directory(${CMAKE_BINARY_DIR}/logs) + # Copy ini files on first build if (NOT EXISTS ${PROJECT_BINARY_DIR}/authconfig.ini) configure_file( diff --git a/README.md b/README.md index c8afb9f0..f3cb6cd7 100644 --- a/README.md +++ b/README.md @@ -236,6 +236,8 @@ The client script for the survival minigame has a bug in it which can cause the * Change `PlayerReady(self)` to `onPlayerReady(self)` * Save the file, overriding readonly mode if required +If you still experience the bug, try deleting/renaming `res/pack/scripts.pak`. + ### Brick-By-Brick building Brick-By-Brick building requires `PATCHSERVERIP=0:` in the `boot.cfg` to point to a HTTP server which always returns `HTTP 404 - Not Found` for all requests. This can be achieved by pointing it to `localhost` while having `sudo python -m http.server 80` running in the background. diff --git a/dAuthServer/AuthServer.cpp b/dAuthServer/AuthServer.cpp index 22f1dfa0..67590fa0 100644 --- a/dAuthServer/AuthServer.cpp +++ b/dAuthServer/AuthServer.cpp @@ -45,6 +45,7 @@ int main(int argc, char** argv) { dConfig config("authconfig.ini"); Game::config = &config; Game::logger->SetLogToConsole(bool(std::stoi(config.GetValue("log_to_console")))); + Game::logger->SetLogDebugStatements(config.GetValue("log_debug_statements") == "1"); //Connect to the MySQL Database std::string mysql_host = config.GetValue("mysql_host"); @@ -152,11 +153,13 @@ int main(int argc, char** argv) { dLogger * SetupLogger() { std::string logPath = "./logs/AuthServer_" + std::to_string(time(nullptr)) + ".log"; bool logToConsole = false; + bool logDebugStatements = false; #ifdef _DEBUG logToConsole = true; + logDebugStatements = true; #endif - return new dLogger(logPath, logToConsole); + return new dLogger(logPath, logToConsole, logDebugStatements); } void HandlePacket(Packet* packet) { diff --git a/dChatServer/ChatServer.cpp b/dChatServer/ChatServer.cpp index be5ec904..81904d41 100644 --- a/dChatServer/ChatServer.cpp +++ b/dChatServer/ChatServer.cpp @@ -48,6 +48,7 @@ int main(int argc, char** argv) { dConfig config("chatconfig.ini"); Game::config = &config; Game::logger->SetLogToConsole(bool(std::stoi(config.GetValue("log_to_console")))); + Game::logger->SetLogDebugStatements(config.GetValue("log_debug_statements") == "1"); //Connect to the MySQL Database std::string mysql_host = config.GetValue("mysql_host"); @@ -156,14 +157,16 @@ int main(int argc, char** argv) { return 0; } -dLogger* SetupLogger() { +dLogger * SetupLogger() { std::string logPath = "./logs/ChatServer_" + std::to_string(time(nullptr)) + ".log"; bool logToConsole = false; + bool logDebugStatements = false; #ifdef _DEBUG logToConsole = true; + logDebugStatements = true; #endif - return new dLogger(logPath, logToConsole); + return new dLogger(logPath, logToConsole, logDebugStatements); } void HandlePacket(Packet* packet) { diff --git a/dCommon/dLogger.cpp b/dCommon/dLogger.cpp index 6ee0e7d2..825c10cb 100644 --- a/dCommon/dLogger.cpp +++ b/dCommon/dLogger.cpp @@ -1,7 +1,8 @@ #include "dLogger.h" -dLogger::dLogger(const std::string& outpath, bool logToConsole) { +dLogger::dLogger(const std::string& outpath, bool logToConsole, bool logDebugStatements) { m_logToConsole = logToConsole; + m_logDebugStatements = logDebugStatements; m_outpath = outpath; #ifdef _WIN32 @@ -24,39 +25,25 @@ dLogger::~dLogger() { #endif } -void dLogger::LogBasic(const std::string & message) { - LogBasic(message.c_str()); -} - -void dLogger::LogBasic(const char * format, ...) { +void dLogger::vLog(const char* format, va_list args) { #ifdef _WIN32 time_t t = time(NULL); struct tm time; localtime_s(&time, &t); - char timeStr[70]; - strftime(timeStr, sizeof(timeStr), "%d-%m-%y %H:%M:%S", &time); - char message[2048]; - va_list args; - va_start(args, format); vsprintf_s(message, format, args); - va_end(args); - if (m_logToConsole) std::cout << "[" << "time machine broke" << "] " << message; - mFile << "[" << "time" << "] " << message; + if (m_logToConsole) std::cout << "[" << timeStr << "] " << message; + mFile << "[" << timeStr << "] " << message; #else time_t t = time(NULL); struct tm * time = localtime(&t); char timeStr[70]; strftime(timeStr, sizeof(timeStr), "%d-%m-%y %H:%M:%S", time); - char message[2048]; - va_list args; - va_start(args, format); vsprintf(message, format, args); - va_end(args); if (m_logToConsole) { fputs("[", stdout); @@ -76,62 +63,42 @@ void dLogger::LogBasic(const char * format, ...) { #endif } -void dLogger::Log(const char * className, const char * format, ...) { -#ifdef _WIN32 - time_t t = time(NULL); - struct tm time; - localtime_s(&time, &t); - - char timeStr[70]; - strftime(timeStr, sizeof(timeStr), "%d-%m-%y %H:%M:%S", &time); - - char message[2048]; +void dLogger::LogBasic(const char * format, ...) { va_list args; va_start(args, format); - vsprintf_s(message, format, args); - + vLog(format, args); va_end(args); +} - if (m_logToConsole) std::cout << "[" << timeStr << "] [" << className << "]: " << message; - mFile << "[" << timeStr << "] [" << className << "]: " << message; -#else - time_t t = time(NULL); - struct tm * time = localtime(&t); - char timeStr[70]; - strftime(timeStr, sizeof(timeStr), "%d-%m-%y %H:%M:%S", time); - - char message[2048]; - va_list args; - va_start(args, format); - vsprintf(message, format, args); - va_end(args); - - if (m_logToConsole) { - fputs("[", stdout); - fputs(timeStr, stdout); - fputs("] ", stdout); - fputs("[", stdout); - fputs(className, stdout); - fputs("]: ", stdout); - fputs(message, stdout); - } - - if (fp != NULL) { - fputs("[", fp); - fputs(timeStr, fp); - fputs("] ", fp); - fputs("[", fp); - fputs(className, fp); - fputs("]: ", fp); - fputs(message, fp); - } -#endif +void dLogger::LogBasic(const std::string & message) { + LogBasic(message.c_str()); +} + +void dLogger::Log(const char * className, const char * format, ...) { + va_list args; + std::string log = "[" + std::string(className) + "] " + std::string(format); + va_start(args, format); + vLog(log.c_str(), args); + va_end(args); } void dLogger::Log(const std::string & className, const std::string & message) { Log(className.c_str(), message.c_str()); } +void dLogger::LogDebug(const char * className, const char * format, ...) { + if (!m_logDebugStatements) return; + va_list args; + std::string log = "[" + std::string(className) + "] " + std::string(format); + va_start(args, format); + vLog(log.c_str(), args); + va_end(args); +} + +void dLogger::LogDebug(const std::string & className, const std::string & message) { + LogDebug(className.c_str(), message.c_str()); +} + void dLogger::Flush() { #ifdef _WIN32 mFile.flush(); diff --git a/dCommon/dLogger.h b/dCommon/dLogger.h index 622a90d3..7448237e 100644 --- a/dCommon/dLogger.h +++ b/dCommon/dLogger.h @@ -7,21 +7,26 @@ class dLogger { public: - dLogger(const std::string& outpath, bool logToConsole); + dLogger(const std::string& outpath, bool logToConsole, bool logDebugStatements); ~dLogger(); void SetLogToConsole(bool logToConsole) { m_logToConsole = logToConsole; } + void SetLogDebugStatements(bool logDebugStatements) { m_logDebugStatements = logDebugStatements; } + void vLog(const char* format, va_list args); void LogBasic(const std::string& message); void LogBasic(const char* format, ...); void Log(const char* className, const char* format, ...); void Log(const std::string& className, const std::string& message); + void LogDebug(const std::string& className, const std::string& message); + void LogDebug(const char* className, const char* format, ...); void Flush(); const bool GetIsLoggingToConsole() const { return m_logToConsole; } private: + bool m_logDebugStatements; bool m_logToConsole; std::string m_outpath; std::ofstream mFile; diff --git a/dGame/Character.h b/dGame/Character.h index 161b9eab..57da8640 100644 --- a/dGame/Character.h +++ b/dGame/Character.h @@ -74,65 +74,127 @@ public: */ uint64_t GetLastLogin() const { return m_LastLogin; } - /** - * Gets the default shirt color for this character - * @return the default shirt color ID - */ - uint32_t GetShirtColor() const { return m_ShirtColor; } + + /** + * Gets the default shirt color for this character + * @return the default shirt color ID + */ + uint32_t GetShirtColor() const { return m_ShirtColor; } - /** - * Gets the default hair style for this character - * @return the default hair style ID - */ - uint32_t GetShirtStyle() const { return m_ShirtStyle; } + /** + * Gets the default hair style for this character + * @return the default hair style ID + */ + uint32_t GetShirtStyle() const { return m_ShirtStyle; } - /** - * Gets the default pants color for this character - * @return the default pants color ID - */ - uint32_t GetPantsColor() const { return m_PantsColor; } + /** + * Gets the default pants color for this character + * @return the default pants color ID + */ + uint32_t GetPantsColor() const { return m_PantsColor; } - /** - * Gets the default hair color for this character - * @return the default hair color ID - */ - uint32_t GetHairColor() const { return m_HairColor; } + /** + * Gets the default hair color for this character + * @return the default hair color ID + */ + uint32_t GetHairColor() const { return m_HairColor; } - /** - * Gets the default hair style of this character - * @return the default hair style ID - */ - uint32_t GetHairStyle() const { return m_HairStyle; } + /** + * Gets the default hair style of this character + * @return the default hair style ID + */ + uint32_t GetHairStyle() const { return m_HairStyle; } - /** - * Gets the eyes config for this character - * @return the eyes config ID - */ - uint32_t GetEyes() const { return m_Eyes; } + /** + * Gets the eyes config for this character + * @return the eyes config ID + */ + uint32_t GetEyes() const { return m_Eyes; } - /** - * Gets the eyebrows config for this character - * @return the eyebrow config ID - */ - uint32_t GetEyebrows() const { return m_Eyebrows; } + /** + * Gets the eyebrows config for this character + * @return the eyebrow config ID + */ + uint32_t GetEyebrows() const { return m_Eyebrows; } - /** - * Get the mouth of this character - * @return the mouth ID - */ - uint32_t GetMouth() const { return m_Mouth; } + /** + * Get the mouth of this character + * @return the mouth ID + */ + uint32_t GetMouth() const { return m_Mouth; } - /** - * Gets the left hand color of this character - * @return the left hand color ID - */ - uint32_t GetLeftHand() const { return m_LeftHand; } + /** + * Gets the left hand color of this character + * @return the left hand color ID + */ + uint32_t GetLeftHand() const { return m_LeftHand; } + + /** + * Gets the right hand color of this character + * @return the right hand color ID + */ + uint32_t GetRightHand() const { return m_RightHand; } + + /** + * Sets the default shirt color for this character + * @param color the shirt color ID to set + */ + void SetShirtColor(uint32_t color) { m_ShirtColor = color; } + + /** + * Sets the default shirt style for this character + * @param style the shirt style ID to set + */ + void SetShirtStyle(uint32_t style) { m_ShirtStyle = style; } + + /** + * Sets the default pants color for this character + * @param color the pants color ID to set + */ + void SetPantsColor(uint32_t color) { m_PantsColor = color; } + + /** + * Sets the default hair color for this character + * @param color the hair color ID to set + */ + void SetHairColor(uint32_t color) { m_HairColor = color; } + + /** + * Sets the default hair style for this character + * @param style the hair style ID to set + */ + void SetHairStyle(uint32_t style) { m_HairStyle = style; } + + /** + * Sets the eyes config for this character + * @param eyes the eyes config ID to set + */ + void SetEyes(uint32_t eyes) { m_Eyes = eyes; } + + /** + * Sets the eyebrows config for this character + * @param eyebrows the eyebrows config ID to set + */ + void SetEyebrows(uint32_t eyebrows) { m_Eyebrows = eyebrows; } + + /** + * Sets the mouth config for this character + * @param mouth the mouth config ID to set + */ + void SetMouth(uint32_t mouth) { m_Mouth = mouth; } + + /** + * Sets the left hand color for this character + * @param color the left hand color ID to set + */ + void SetLeftHand(uint32_t leftHand) { m_LeftHand = leftHand; } + + /** + * Sets the right hand color for this character + * @param color the right hand color ID to set + */ + void SetRightHand(uint32_t rightHand) { m_RightHand = rightHand; } - /** - * Gets the right hand color of this character - * @return the right hand color ID - */ - uint32_t GetRightHand() const { return m_RightHand; } /** * Whether this character has visited a certain zone diff --git a/dGame/Entity.cpp b/dGame/Entity.cpp index c98323a9..dd6df1d4 100644 --- a/dGame/Entity.cpp +++ b/dGame/Entity.cpp @@ -1644,6 +1644,26 @@ void Entity::PickupItem(const LWOOBJID& objectID) { droppedLoot.erase(objectID); } +bool Entity::CanPickupCoins(uint64_t count) { + if (!IsPlayer()) return false; + auto* player = static_cast(this); + auto droppedCoins = player->GetDroppedCoins(); + if (count > droppedCoins) { + return false; + } else { + player->SetDroppedCoins(droppedCoins - count); + return true; + } +} + +void Entity::RegisterCoinDrop(uint64_t count) { + if (!IsPlayer()) return; + auto* player = static_cast(this); + auto droppedCoins = player->GetDroppedCoins(); + droppedCoins += count; + player->SetDroppedCoins(droppedCoins); +} + void Entity::AddChild(Entity* child) { m_ChildEntities.push_back(child); } @@ -1768,6 +1788,7 @@ void Entity::HandleTriggerCommand(std::string id, std::string target, std::strin else if (argArray[0] == "repulse") effectType = 2; else if (argArray[0] == "gravity") effectType = 3; else if (argArray[0] == "friction") effectType = 4; + phanPhys->SetEffectType(effectType); phanPhys->SetDirectionalMultiplier(std::stof(argArray[1])); if (argArray.size() > 4) { @@ -1781,6 +1802,10 @@ void Entity::HandleTriggerCommand(std::string id, std::string target, std::strin phanPhys->SetMin(std::stoi(argArray[6])); phanPhys->SetMax(std::stoi(argArray[7])); } + + if (target == "self") { + EntityManager::Instance()->ConstructEntity(this); + } } else if (id == "updateMission") { CDMissionTasksTable* missionTasksTable = CDClientManager::Instance()->GetTable("MissionTasks"); diff --git a/dGame/Entity.h b/dGame/Entity.h index 01699a84..31b2b303 100644 --- a/dGame/Entity.h +++ b/dGame/Entity.h @@ -202,6 +202,9 @@ public: void AddLootItem(const Loot::Info& info); void PickupItem(const LWOOBJID& objectID); + bool CanPickupCoins(uint64_t count); + void RegisterCoinDrop(uint64_t count); + void ScheduleKillAfterUpdate(Entity* murderer = nullptr); void TriggerEvent(std::string eveneventtID, Entity* optionalTarget = nullptr); void ScheduleDestructionAfterUpdate() { m_ShouldDestroyAfterUpdate = true; } diff --git a/dGame/Player.cpp b/dGame/Player.cpp index 9a158c8f..634d4a68 100644 --- a/dGame/Player.cpp +++ b/dGame/Player.cpp @@ -24,6 +24,7 @@ Player::Player(const LWOOBJID& objectID, const EntityInfo info, User* user, Enti m_GMLevel = m_Character->GetGMLevel(); m_SystemAddress = m_ParentUser->GetSystemAddress(); m_DroppedLoot = {}; + m_DroppedCoins = 0; m_GhostReferencePoint = NiPoint3::ZERO; m_GhostOverridePoint = NiPoint3::ZERO; @@ -290,6 +291,14 @@ const std::vector& Player::GetAllPlayers() return m_Players; } +uint64_t Player::GetDroppedCoins() { + return m_DroppedCoins; +} + +void Player::SetDroppedCoins(uint64_t value) { + m_DroppedCoins = value; +} + Player::~Player() { Game::logger->Log("Player", "Deleted player\n"); diff --git a/dGame/Player.h b/dGame/Player.h index abd811c5..bba01363 100644 --- a/dGame/Player.h +++ b/dGame/Player.h @@ -36,6 +36,8 @@ public: std::map& GetDroppedLoot(); + uint64_t GetDroppedCoins(); + /** * Setters */ @@ -52,6 +54,8 @@ public: void SetGhostOverride(bool value); + void SetDroppedCoins(uint64_t value); + /** * Wrapper for sending an in-game mail. * @@ -126,5 +130,7 @@ private: std::map m_DroppedLoot; + uint64_t m_DroppedCoins; + static std::vector m_Players; }; diff --git a/dGame/dBehaviors/AreaOfEffectBehavior.cpp b/dGame/dBehaviors/AreaOfEffectBehavior.cpp index a5341348..b87c7c17 100644 --- a/dGame/dBehaviors/AreaOfEffectBehavior.cpp +++ b/dGame/dBehaviors/AreaOfEffectBehavior.cpp @@ -74,7 +74,7 @@ void AreaOfEffectBehavior::Calculate(BehaviorContext* context, RakNet::BitStream includeFaction = 1; } - for (auto validTarget : context->GetValidTargets(m_ignoreFaction , includeFaction)) + for (auto validTarget : context->GetValidTargets(m_ignoreFaction , includeFaction, m_TargetSelf == 1)) { auto* entity = EntityManager::Instance()->GetEntity(validTarget); @@ -155,4 +155,6 @@ void AreaOfEffectBehavior::Load() this->m_ignoreFaction = GetInt("ignore_faction"); this->m_includeFaction = GetInt("include_faction"); + + this->m_TargetSelf = GetInt("target_self"); } diff --git a/dGame/dBehaviors/AreaOfEffectBehavior.h b/dGame/dBehaviors/AreaOfEffectBehavior.h index c2b6679d..37313499 100644 --- a/dGame/dBehaviors/AreaOfEffectBehavior.h +++ b/dGame/dBehaviors/AreaOfEffectBehavior.h @@ -13,6 +13,8 @@ public: int32_t m_ignoreFaction; int32_t m_includeFaction; + + int32_t m_TargetSelf; /* * Inherited diff --git a/dGame/dBehaviors/BehaviorContext.cpp b/dGame/dBehaviors/BehaviorContext.cpp index fd3117a4..2637aa61 100644 --- a/dGame/dBehaviors/BehaviorContext.cpp +++ b/dGame/dBehaviors/BehaviorContext.cpp @@ -149,7 +149,7 @@ void BehaviorContext::SyncBehavior(const uint32_t syncId, RakNet::BitStream* bit return; } - + behavior->Sync(this, bitStream, branch); } @@ -325,7 +325,7 @@ void BehaviorContext::Reset() this->scheduledUpdates.clear(); } -std::vector BehaviorContext::GetValidTargets(int32_t ignoreFaction, int32_t includeFaction) const +std::vector BehaviorContext::GetValidTargets(int32_t ignoreFaction, int32_t includeFaction, bool targetSelf) const { auto* entity = EntityManager::Instance()->GetEntity(this->caster); @@ -353,21 +353,20 @@ std::vector BehaviorContext::GetValidTargets(int32_t ignoreFaction, in } } - if (ignoreFaction || includeFaction || (!entity->HasComponent(COMPONENT_TYPE_PHANTOM_PHYSICS) && !entity->HasComponent(COMPONENT_TYPE_CONTROLLABLE_PHYSICS) && targets.empty())) + if (ignoreFaction || includeFaction || (!entity->HasComponent(COMPONENT_TYPE_PHANTOM_PHYSICS) && targets.empty())) { - DestroyableComponent* destroyableComponent; + DestroyableComponent* destroyableComponent; if (!entity->TryGetComponent(COMPONENT_TYPE_DESTROYABLE, destroyableComponent)) { return targets; } auto entities = EntityManager::Instance()->GetEntitiesByComponent(COMPONENT_TYPE_CONTROLLABLE_PHYSICS); - for (auto* candidate : entities) { const auto id = candidate->GetObjectID(); - if (destroyableComponent->CheckValidity(id, ignoreFaction || includeFaction)) + if ((id != entity->GetObjectID() || targetSelf) && destroyableComponent->CheckValidity(id, ignoreFaction || includeFaction)) { targets.push_back(id); } diff --git a/dGame/dBehaviors/BehaviorContext.h b/dGame/dBehaviors/BehaviorContext.h index 58154512..21b2e6e9 100644 --- a/dGame/dBehaviors/BehaviorContext.h +++ b/dGame/dBehaviors/BehaviorContext.h @@ -102,7 +102,7 @@ struct BehaviorContext void Reset(); - std::vector GetValidTargets(int32_t ignoreFaction = 0, int32_t includeFaction = 0) const; + std::vector GetValidTargets(int32_t ignoreFaction = 0, int32_t includeFaction = 0, const bool targetSelf = false) const; explicit BehaviorContext(LWOOBJID originator, bool calculation = false); diff --git a/dGame/dBehaviors/ForceMovementBehavior.cpp b/dGame/dBehaviors/ForceMovementBehavior.cpp index 332cfed6..ea8c04a7 100644 --- a/dGame/dBehaviors/ForceMovementBehavior.cpp +++ b/dGame/dBehaviors/ForceMovementBehavior.cpp @@ -1,44 +1,81 @@ #include "ForceMovementBehavior.h" - #include "BehaviorBranchContext.h" #include "BehaviorContext.h" +#include "ControllablePhysicsComponent.h" +#include "EntityManager.h" -void ForceMovementBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bitStream, const BehaviorBranchContext branch) -{ - if (this->m_hitAction->m_templateId == BehaviorTemplates::BEHAVIOR_EMPTY && this->m_hitEnemyAction->m_templateId == BehaviorTemplates::BEHAVIOR_EMPTY && this->m_hitFactionAction->m_templateId == BehaviorTemplates::BEHAVIOR_EMPTY) - { - return; - } +void ForceMovementBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bitStream, const BehaviorBranchContext branch) { + if (this->m_hitAction->m_templateId == BehaviorTemplates::BEHAVIOR_EMPTY && this->m_hitEnemyAction->m_templateId == BehaviorTemplates::BEHAVIOR_EMPTY && this->m_hitFactionAction->m_templateId == BehaviorTemplates::BEHAVIOR_EMPTY) { + return; + } - uint32_t handle; - - bitStream->Read(handle); - - context->RegisterSyncBehavior(handle, this, branch); + uint32_t handle; + bitStream->Read(handle); + context->RegisterSyncBehavior(handle, this, branch); } void ForceMovementBehavior::Sync(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) { - uint32_t next; + uint32_t next; + bitStream->Read(next); - bitStream->Read(next); + LWOOBJID target; + bitStream->Read(target); - LWOOBJID target; + branch.target = target; + auto* behavior = CreateBehavior(next); + behavior->Handle(context, bitStream, branch); +} - bitStream->Read(target); +void ForceMovementBehavior::Calculate(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) { + if (this->m_hitAction->m_templateId == BehaviorTemplates::BEHAVIOR_EMPTY && this->m_hitEnemyAction->m_templateId == BehaviorTemplates::BEHAVIOR_EMPTY && this->m_hitFactionAction->m_templateId == BehaviorTemplates::BEHAVIOR_EMPTY) { + return; + } - auto* behavior = CreateBehavior(next); + auto* casterEntity = EntityManager::Instance()->GetEntity(context->caster); + if (casterEntity != nullptr) { + auto* controllablePhysicsComponent = casterEntity->GetComponent(); + if (controllablePhysicsComponent != nullptr) { - branch.target = target; + if (m_Forward == 1) { + controllablePhysicsComponent->SetVelocity(controllablePhysicsComponent->GetRotation().GetForwardVector() * 25); + } - behavior->Handle(context, bitStream, branch); + EntityManager::Instance()->SerializeEntity(casterEntity); + } + } + + const auto skillHandle = context->GetUniqueSkillId(); + bitStream->Write(skillHandle); + + context->SyncCalculation(skillHandle, this->m_Duration, this, branch); } void ForceMovementBehavior::Load() { - this->m_hitAction = GetAction("hit_action"); - - this->m_hitEnemyAction = GetAction("hit_action_enemy"); - - this->m_hitFactionAction = GetAction("hit_action_faction"); + this->m_hitAction = GetAction("hit_action"); + this->m_hitEnemyAction = GetAction("hit_action_enemy"); + this->m_hitFactionAction = GetAction("hit_action_faction"); + this->m_Duration = GetFloat("duration"); + this->m_Forward = GetFloat("forward"); + this->m_Left = GetFloat("left"); + this->m_Yaw = GetFloat("yaw"); +} + +void ForceMovementBehavior::SyncCalculation(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) { + auto* casterEntity = EntityManager::Instance()->GetEntity(context->caster); + if (casterEntity != nullptr) { + auto* controllablePhysicsComponent = casterEntity->GetComponent(); + if (controllablePhysicsComponent != nullptr) { + + controllablePhysicsComponent->SetPosition(controllablePhysicsComponent->GetPosition() + controllablePhysicsComponent->GetVelocity() * m_Duration); + controllablePhysicsComponent->SetVelocity({}); + + EntityManager::Instance()->SerializeEntity(casterEntity); + } + } + + this->m_hitAction->Calculate(context, bitStream, branch); + this->m_hitEnemyAction->Calculate(context, bitStream, branch); + this->m_hitEnemyAction->Calculate(context, bitStream, branch); } diff --git a/dGame/dBehaviors/ForceMovementBehavior.h b/dGame/dBehaviors/ForceMovementBehavior.h index 5b77e4b7..50b0aa26 100644 --- a/dGame/dBehaviors/ForceMovementBehavior.h +++ b/dGame/dBehaviors/ForceMovementBehavior.h @@ -9,6 +9,11 @@ public: Behavior* m_hitEnemyAction; Behavior* m_hitFactionAction; + + float_t m_Duration; + float_t m_Forward; + float_t m_Left; + float_t m_Yaw; /* * Inherited @@ -18,8 +23,12 @@ public: { } + void Calculate(BehaviorContext *context, RakNet::BitStream *bitStream, BehaviorBranchContext branch) override; + void Handle(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) override; + void SyncCalculation(BehaviorContext *context, RakNet::BitStream *bitStream, BehaviorBranchContext branch) override; + void Sync(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) override; void Load() override; diff --git a/dGame/dComponents/BaseCombatAIComponent.cpp b/dGame/dComponents/BaseCombatAIComponent.cpp index 44154f52..ae929d57 100644 --- a/dGame/dComponents/BaseCombatAIComponent.cpp +++ b/dGame/dComponents/BaseCombatAIComponent.cpp @@ -129,6 +129,9 @@ BaseCombatAIComponent::BaseCombatAIComponent(Entity* parent, const uint32_t id) BaseCombatAIComponent::~BaseCombatAIComponent() { if (m_dpEntity) dpWorld::Instance().RemoveEntity(m_dpEntity); + + if (m_dpEntityEnemy) + dpWorld::Instance().RemoveEntity(m_dpEntityEnemy); } void BaseCombatAIComponent::Update(const float deltaTime) { diff --git a/dGame/dComponents/CharacterComponent.cpp b/dGame/dComponents/CharacterComponent.cpp index d2335c78..9337c324 100644 --- a/dGame/dComponents/CharacterComponent.cpp +++ b/dGame/dComponents/CharacterComponent.cpp @@ -352,6 +352,26 @@ void CharacterComponent::LoadFromXML() { } void CharacterComponent::UpdateXml(tinyxml2::XMLDocument* doc) { + tinyxml2::XMLElement* minifig = doc->FirstChildElement("obj")->FirstChildElement("mf"); + if (!minifig) { + Game::logger->Log("CharacterComponent", "Failed to find mf tag while updating XML!\n"); + return; + } + + // write minifig information that might have been changed by commands + + minifig->SetAttribute("es", m_Character->GetEyebrows()); + minifig->SetAttribute("ess", m_Character->GetEyes()); + minifig->SetAttribute("hc", m_Character->GetHairColor()); + minifig->SetAttribute("hs", m_Character->GetHairStyle()); + minifig->SetAttribute("l", m_Character->GetPantsColor()); + minifig->SetAttribute("lh", m_Character->GetLeftHand()); + minifig->SetAttribute("ms", m_Character->GetMouth()); + minifig->SetAttribute("rh", m_Character->GetRightHand()); + minifig->SetAttribute("t", m_Character->GetShirtColor()); + + // done with minifig + tinyxml2::XMLElement* character = doc->FirstChildElement("obj")->FirstChildElement("char"); if (!character) { Game::logger->Log("CharacterComponent", "Failed to find char tag while updating XML!\n"); diff --git a/dGame/dComponents/CharacterComponent.h b/dGame/dComponents/CharacterComponent.h index 26b88a57..62076fad 100644 --- a/dGame/dComponents/CharacterComponent.h +++ b/dGame/dComponents/CharacterComponent.h @@ -259,11 +259,12 @@ public: */ void UpdatePlayerStatistic(StatisticID updateID, uint64_t updateValue = 1); -private: /** * Character info regarding this character, including clothing styles, etc. */ - Character* m_Character; + Character* m_Character; +private: + /** * Whether this character is racing diff --git a/dGame/dComponents/ControllablePhysicsComponent.h b/dGame/dComponents/ControllablePhysicsComponent.h index 50ec4f26..1c1a4f44 100644 --- a/dGame/dComponents/ControllablePhysicsComponent.h +++ b/dGame/dComponents/ControllablePhysicsComponent.h @@ -7,6 +7,8 @@ #include "NiQuaternion.h" #include "tinyxml2.h" #include "Component.h" +#include "dpCollisionChecks.h" +#include "PhantomPhysicsComponent.h" class Entity; class dpEntity; @@ -218,6 +220,13 @@ public: */ bool GetStatic() const { return m_Static; } + /** + * Returns the Physics entity for the component + * @return Physics entity for the component + */ + + dpEntity* GetdpEntity() const { return m_dpEntity; } + private: /** * The entity that owns this component diff --git a/dGame/dComponents/PhantomPhysicsComponent.h b/dGame/dComponents/PhantomPhysicsComponent.h index fe1ee242..faf6362f 100644 --- a/dGame/dComponents/PhantomPhysicsComponent.h +++ b/dGame/dComponents/PhantomPhysicsComponent.h @@ -110,6 +110,13 @@ public: */ void SetEffectType(uint32_t type); + /** + * Returns the Physics entity for the component + * @return Physics entity for the component + */ + + dpEntity* GetdpEntity() const { return m_dpEntity; } + /** * Spawns an object at each of the vertices for debugging purposes */ diff --git a/dGame/dGameMessages/GameMessages.cpp b/dGame/dGameMessages/GameMessages.cpp index c6b3c9bd..fad5d7de 100644 --- a/dGame/dGameMessages/GameMessages.cpp +++ b/dGame/dGameMessages/GameMessages.cpp @@ -1031,6 +1031,10 @@ void GameMessages::SendDropClientLoot(Entity* entity, const LWOOBJID& sourceID, entity->AddLootItem(info); } + if (item == LOT_NULL && currency != 0) { + entity->RegisterCoinDrop(currency); + } + if (spawnPos != NiPoint3::ZERO) { bUsePosition = true; @@ -5232,8 +5236,12 @@ void GameMessages::HandlePickupCurrency(RakNet::BitStream* inStream, Entity* ent unsigned int currency; inStream->Read(currency); + if (currency == 0) return; + auto* ch = entity->GetCharacter(); - ch->SetCoins(ch->GetCoins() + currency); + if (entity->CanPickupCoins(currency)) { + ch->SetCoins(ch->GetCoins() + currency); + } } void GameMessages::HandleRequestDie(RakNet::BitStream* inStream, Entity* entity) { diff --git a/dGame/dUtilities/SlashCommandHandler.cpp b/dGame/dUtilities/SlashCommandHandler.cpp index 4a278230..adcd5c30 100644 --- a/dGame/dUtilities/SlashCommandHandler.cpp +++ b/dGame/dUtilities/SlashCommandHandler.cpp @@ -407,6 +407,50 @@ void SlashCommandHandler::HandleChatCommand(const std::u16string& command, Entit stmt->execute(); delete stmt; + if (chatCommand == "setMinifig" && args.size() == 2 && entity->GetGMLevel() >= GAME_MASTER_LEVEL_FORUM_MODERATOR) { // could break characters so only allow if GM > 0 + int32_t minifigItemId; + if (!GeneralUtils::TryParse(args[1], minifigItemId)) { + ChatPackets::SendSystemMessage(sysAddr, u"Invalid Minifig Item Id ID."); + return; + } + EntityManager::Instance()->DestructEntity(entity, sysAddr); + auto* charComp = entity->GetComponent(); + std::string lowerName = args[0]; + if (lowerName.empty()) return; + std::transform(lowerName.begin(), lowerName.end(), lowerName.begin(), ::tolower); + if (lowerName == "eyebrows") { + charComp->m_Character->SetEyebrows(minifigItemId); + } else if (lowerName == "eyes") { + charComp->m_Character->SetEyes(minifigItemId); + } else if (lowerName == "haircolor") { + charComp->m_Character->SetHairColor(minifigItemId); + } else if (lowerName == "hairstyle") { + charComp->m_Character->SetHairStyle(minifigItemId); + } else if (lowerName == "pants") { + charComp->m_Character->SetPantsColor(minifigItemId); + } else if (lowerName == "lefthand") { + charComp->m_Character->SetLeftHand(minifigItemId); + } else if (lowerName == "mouth") { + charComp->m_Character->SetMouth(minifigItemId); + } else if (lowerName == "righthand") { + charComp->m_Character->SetRightHand(minifigItemId); + } else if (lowerName == "shirt") { + charComp->m_Character->SetShirtColor(minifigItemId); + } else if (lowerName == "hands") { + charComp->m_Character->SetLeftHand(minifigItemId); + charComp->m_Character->SetRightHand(minifigItemId); + } else { + EntityManager::Instance()->ConstructEntity(entity); + ChatPackets::SendSystemMessage(sysAddr, u"Invalid Minifig item to change, try one of the following: Eyebrows, Eyes, HairColor, HairStyle, Pants, LeftHand, Mouth, RightHand, Shirt, Hands"); + return; + } + + EntityManager::Instance()->ConstructEntity(entity); + ChatPackets::SendSystemMessage(sysAddr, GeneralUtils::ASCIIToUTF16(lowerName) + u" set to " + (GeneralUtils::to_u16string(minifigItemId))); + + GameMessages::SendToggleGMInvis(entity->GetObjectID(), false, UNASSIGNED_SYSTEM_ADDRESS); // need to retoggle because it gets reenabled on creation of new character + } + if (chatCommand == "list-spawns" && entity->GetGMLevel() >= GAME_MASTER_LEVEL_DEVELOPER) { for (const auto& pair : EntityManager::Instance()->GetSpawnPointEntities()) { ChatPackets::SendSystemMessage(sysAddr, GeneralUtils::ASCIIToUTF16(pair.first)); @@ -429,6 +473,10 @@ void SlashCommandHandler::HandleChatCommand(const std::u16string& command, Entit entity->GetCharacter()->UnlockEmote(emoteID); } + if (chatCommand == "force-save" && entity->GetGMLevel() >= GAME_MASTER_LEVEL_DEVELOPER) { + entity->GetCharacter()->SaveXMLToDatabase(); + } + if (chatCommand == "kill" && args.size() == 1 && entity->GetGMLevel() >= GAME_MASTER_LEVEL_DEVELOPER) { ChatPackets::SendSystemMessage(sysAddr, u"Brutally murdering that player, if online on this server."); diff --git a/dMasterServer/MasterServer.cpp b/dMasterServer/MasterServer.cpp index 8f89d8c8..af585272 100644 --- a/dMasterServer/MasterServer.cpp +++ b/dMasterServer/MasterServer.cpp @@ -72,6 +72,7 @@ int main(int argc, char** argv) { dConfig config("masterconfig.ini"); Game::config = &config; Game::logger->SetLogToConsole(bool(std::stoi(config.GetValue("log_to_console")))); + Game::logger->SetLogDebugStatements(config.GetValue("log_debug_statements") == "1"); //Connect to CDClient try { @@ -326,11 +327,13 @@ dLogger* SetupLogger() { std::string logPath = "./logs/MasterServer_" + std::to_string(time(nullptr)) + ".log"; bool logToConsole = false; + bool logDebugStatements = false; #ifdef _DEBUG logToConsole = true; + logDebugStatements = true; #endif - return new dLogger(logPath, logToConsole); + return new dLogger(logPath, logToConsole, logDebugStatements); } void HandlePacket(Packet* packet) { diff --git a/dScripts/BuccaneerValiantShip.cpp b/dScripts/BuccaneerValiantShip.cpp new file mode 100644 index 00000000..2c24b7d4 --- /dev/null +++ b/dScripts/BuccaneerValiantShip.cpp @@ -0,0 +1,24 @@ +#include "BuccaneerValiantShip.h" +#include "SkillComponent.h" +#include "dLogger.h" + +void BuccaneerValiantShip::OnStartup(Entity* self) { + const auto skill = 982; + const auto behavior = 20577; + const auto skillCastTimer = 1.0F; + + self->AddCallbackTimer(skillCastTimer, [self]() { + auto* skillComponent = self->GetComponent(); + auto* owner = self->GetOwner(); + + if (skillComponent != nullptr && owner != nullptr) { + skillComponent->CalculateBehavior(skill, behavior, LWOOBJID_EMPTY, true, false, owner->GetObjectID()); + + // Kill self if missed + const auto selfSmashTimer = 1.1F; + self->AddCallbackTimer(selfSmashTimer, [self]() { + self->Kill(); + }); + } + }); +} diff --git a/dScripts/BuccaneerValiantShip.h b/dScripts/BuccaneerValiantShip.h new file mode 100644 index 00000000..f501d1b9 --- /dev/null +++ b/dScripts/BuccaneerValiantShip.h @@ -0,0 +1,6 @@ +#pragma once +#include "CppScripts.h" + +class BuccaneerValiantShip : public CppScripts::Script { + void OnStartup(Entity *self) override; +}; diff --git a/dScripts/CppScripts.cpp b/dScripts/CppScripts.cpp index ed59c5ba..1db8b8dc 100644 --- a/dScripts/CppScripts.cpp +++ b/dScripts/CppScripts.cpp @@ -261,6 +261,7 @@ #include "PersonalFortress.h" #include "PropertyDevice.h" #include "ImaginationBackpackHealServer.h" +#include "BuccaneerValiantShip.h" // Survival scripts #include "AgSurvivalStromling.h" @@ -774,6 +775,8 @@ CppScripts::Script* CppScripts::GetScript(Entity* parent, const std::string& scr script = new PropertyDevice(); else if (scriptName == "scripts\\02_server\\Map\\General\\L_IMAG_BACKPACK_HEALS_SERVER.lua") script = new ImaginationBackpackHealServer(); + else if (scriptName == "scripts\\EquipmentScripts\\BuccaneerValiantShip.lua") + script = new BuccaneerValiantShip(); //Ignore these scripts: else if (scriptName == "scripts\\02_server\\Enemy\\General\\L_SUSPEND_LUA_AI.lua") @@ -782,7 +785,8 @@ CppScripts::Script* CppScripts::GetScript(Entity* parent, const std::string& scr script = invalidToReturn; else if (script == invalidToReturn) { if (scriptName.length() > 0) - Game::logger->Log("CppScripts", "Attempted to load CppScript for '" + scriptName + "', but returned InvalidScript.\n"); + Game::logger->LogDebug("CppScripts", "Attempted to load CppScript for '" + scriptName + "', but returned InvalidScript.\n"); + // information not really needed for sys admins but is for developers script = invalidToReturn; } diff --git a/dScripts/NsTokenConsoleServer.cpp b/dScripts/NsTokenConsoleServer.cpp index 90b5ed1d..f6b022d0 100644 --- a/dScripts/NsTokenConsoleServer.cpp +++ b/dScripts/NsTokenConsoleServer.cpp @@ -50,19 +50,19 @@ void NsTokenConsoleServer::OnUse(Entity* self, Entity* user) if (character->GetPlayerFlag(46)) { - inventoryComponent->AddItem(8321, 1); + inventoryComponent->AddItem(8321, 5); } else if (character->GetPlayerFlag(47)) { - inventoryComponent->AddItem(8318, 1); + inventoryComponent->AddItem(8318, 5); } else if (character->GetPlayerFlag(48)) { - inventoryComponent->AddItem(8320, 1); + inventoryComponent->AddItem(8320, 5); } else if (character->GetPlayerFlag(49)) { - inventoryComponent->AddItem(8319, 1); + inventoryComponent->AddItem(8319, 5); } missionComponent->ForceProgressTaskType(863, 1, 1, false); diff --git a/dWorldServer/WorldServer.cpp b/dWorldServer/WorldServer.cpp index 5d155f29..ed751f41 100644 --- a/dWorldServer/WorldServer.cpp +++ b/dWorldServer/WorldServer.cpp @@ -136,6 +136,7 @@ int main(int argc, char** argv) { dConfig config("worldconfig.ini"); Game::config = &config; Game::logger->SetLogToConsole(bool(std::stoi(config.GetValue("log_to_console")))); + Game::logger->SetLogDebugStatements(config.GetValue("log_debug_statements") == "1"); if (config.GetValue("disable_chat") == "1") chatDisabled = true; // Connect to CDClient @@ -503,11 +504,13 @@ int main(int argc, char** argv) { dLogger * SetupLogger(int zoneID, int instanceID) { std::string logPath = "./logs/WorldServer_" + std::to_string(zoneID) + "_" + std::to_string(instanceID) + "_" + std::to_string(time(nullptr)) + ".log"; bool logToConsole = false; + bool logDebugStatements = false; #ifdef _DEBUG logToConsole = true; + logDebugStatements = true; #endif - return new dLogger(logPath, logToConsole); + return new dLogger(logPath, logToConsole, logDebugStatements); } void HandlePacketChat(Packet* packet) { diff --git a/resources/authconfig.ini b/resources/authconfig.ini index fd1fc5c4..40ca146e 100644 --- a/resources/authconfig.ini +++ b/resources/authconfig.ini @@ -19,6 +19,9 @@ max_clients=999 # 0 or 1, should log to console log_to_console=1 +# 0 or 1, should log debug (developer only) statements to console for debugging, not needed for normal operation +log_debug_statements=0 + # 0 or 1, should ignore playkeys # If 1 everyone with an account will be able to login, regardless of if they have a key or not dont_use_keys=0 diff --git a/resources/chatconfig.ini b/resources/chatconfig.ini index 28db5988..f30fb8f9 100644 --- a/resources/chatconfig.ini +++ b/resources/chatconfig.ini @@ -19,5 +19,8 @@ max_clients=999 # 0 or 1, should log to console log_to_console=1 +# 0 or 1, should log debug (developer only) statements to console for debugging, not needed for normal operation +log_debug_statements=0 + # 0 or 1, should not compile chat hash map to file dont_generate_dcf=0 diff --git a/resources/masterconfig.ini b/resources/masterconfig.ini index 531234b6..c2d884a5 100644 --- a/resources/masterconfig.ini +++ b/resources/masterconfig.ini @@ -35,5 +35,8 @@ max_clients=999 # 0 or 1, should log to console log_to_console=1 +# 0 or 1, should log debug (developer only) statements to console for debugging, not needed for normal operation +log_debug_statements=0 + # 0 or 1, should autostart auth, chat, and char servers prestart_servers=1 diff --git a/resources/worldconfig.ini b/resources/worldconfig.ini index e5932ec7..e30aa865 100644 --- a/resources/worldconfig.ini +++ b/resources/worldconfig.ini @@ -20,6 +20,9 @@ max_clients=999 # 0 or 1, should log to console log_to_console=1 +# 0 or 1, should log debug (developer only) statements to console for debugging, not needed for normal operation +log_debug_statements=0 + # 0 or 1, should not compile chat hash map to file dont_generate_dcf=0