From d6cac65a8dbb9254950ae0b28b612baf4b0ff193 Mon Sep 17 00:00:00 2001 From: TAHuntling <38479763+TAHuntling@users.noreply.github.com> Date: Tue, 21 May 2024 20:01:44 -0500 Subject: [PATCH 1/5] fix: Falling Off Edge in Pet Puzzle (#1584) * FloatFix * game activity setting * Update dNavMesh.cpp --------- Co-authored-by: David Markowitz --- dGame/dComponents/PetComponent.cpp | 41 +++++++++++++++++++++++------- dNavigation/dNavMesh.cpp | 25 ++++++++++++++++++ dNavigation/dNavMesh.h | 4 +-- 3 files changed, 59 insertions(+), 11 deletions(-) diff --git a/dGame/dComponents/PetComponent.cpp b/dGame/dComponents/PetComponent.cpp index debe0bd8..073c09e1 100644 --- a/dGame/dComponents/PetComponent.cpp +++ b/dGame/dComponents/PetComponent.cpp @@ -32,6 +32,8 @@ #include "eGameMasterLevel.h" #include "eMissionState.h" #include "dNavMesh.h" +#include "eGameActivity.h" +#include "eStateChangeType.h" std::unordered_map PetComponent::currentActivities{}; std::unordered_map PetComponent::activePets{}; @@ -210,24 +212,23 @@ void PetComponent::OnUse(Entity* originator) { if (dpWorld::IsLoaded()) { NiPoint3 attempt = petPosition + forward * interactionDistance; - float y = dpWorld::GetNavMesh()->GetHeightAtPoint(attempt); + NiPoint3 nearestPoint = dpWorld::GetNavMesh()->NearestPoint(attempt); - while (std::abs(y - petPosition.y) > 4 && interactionDistance > 10) { + while (std::abs(nearestPoint.y - petPosition.y) > 4 && interactionDistance > 10) { const NiPoint3 forward = m_Parent->GetRotation().GetForwardVector(); attempt = originatorPosition + forward * interactionDistance; - y = dpWorld::GetNavMesh()->GetHeightAtPoint(attempt); + nearestPoint = dpWorld::GetNavMesh()->NearestPoint(attempt); interactionDistance -= 0.5f; } - position = attempt; + position = nearestPoint; } else { position = petPosition + forward * interactionDistance; } - auto rotation = NiQuaternion::LookAt(position, petPosition); GameMessages::SendNotifyPetTamingMinigame( @@ -246,11 +247,11 @@ void PetComponent::OnUse(Entity* originator) { m_Parent->GetObjectID(), LWOOBJID_EMPTY, originator->GetObjectID(), - true, + false, ePetTamingNotifyType::BEGIN, - petPosition, - position, - rotation, + NiPoint3Constant::ZERO, + NiPoint3Constant::ZERO, + NiQuaternion(0.0f, 0.0f, 0.0f, 0.0f), UNASSIGNED_SYSTEM_ADDRESS ); @@ -258,11 +259,18 @@ void PetComponent::OnUse(Entity* originator) { m_Tamer = originator->GetObjectID(); SetStatus(5); + Game::entityManager->SerializeEntity(m_Parent); currentActivities.insert_or_assign(m_Tamer, m_Parent->GetObjectID()); // Notify the start of a pet taming minigame m_Parent->GetScript()->OnNotifyPetTamingMinigame(m_Parent, originator, ePetTamingNotifyType::BEGIN); + + auto* characterComponent = originator->GetComponent(); + if (characterComponent != nullptr) { + characterComponent->SetCurrentActivity(eGameActivity::PET_TAMING); + Game::entityManager->SerializeEntity(originator); + } } void PetComponent::Update(float deltaTime) { @@ -627,6 +635,11 @@ void PetComponent::RequestSetPetName(std::u16string name) { UNASSIGNED_SYSTEM_ADDRESS ); + auto* characterComponent = tamer->GetComponent(); + if (characterComponent != nullptr) { + characterComponent->SetCurrentActivity(eGameActivity::NONE); + Game::entityManager->SerializeEntity(tamer); + } GameMessages::SendTerminateInteraction(m_Tamer, eTerminateType::FROM_INTERACTION, m_Parent->GetObjectID()); auto* modelEntity = Game::entityManager->GetEntity(m_ModelId); @@ -666,6 +679,11 @@ void PetComponent::ClientExitTamingMinigame(bool voluntaryExit) { UNASSIGNED_SYSTEM_ADDRESS ); + auto* characterComponent = tamer->GetComponent(); + if (characterComponent != nullptr) { + characterComponent->SetCurrentActivity(eGameActivity::NONE); + Game::entityManager->SerializeEntity(tamer); + } GameMessages::SendNotifyTamingModelLoadedOnServer(m_Tamer, tamer->GetSystemAddress()); GameMessages::SendTerminateInteraction(m_Tamer, eTerminateType::FROM_INTERACTION, m_Parent->GetObjectID()); @@ -712,6 +730,11 @@ void PetComponent::ClientFailTamingMinigame() { UNASSIGNED_SYSTEM_ADDRESS ); + auto* characterComponent = tamer->GetComponent(); + if (characterComponent != nullptr) { + characterComponent->SetCurrentActivity(eGameActivity::NONE); + Game::entityManager->SerializeEntity(tamer); + } GameMessages::SendNotifyTamingModelLoadedOnServer(m_Tamer, tamer->GetSystemAddress()); GameMessages::SendTerminateInteraction(m_Tamer, eTerminateType::FROM_INTERACTION, m_Parent->GetObjectID()); diff --git a/dNavigation/dNavMesh.cpp b/dNavigation/dNavMesh.cpp index f49dd31e..d9584b00 100644 --- a/dNavigation/dNavMesh.cpp +++ b/dNavigation/dNavMesh.cpp @@ -112,6 +112,31 @@ void dNavMesh::LoadNavmesh() { m_NavMesh = mesh; } +NiPoint3 dNavMesh::NearestPoint(const NiPoint3& location, const float halfExtent) const { + NiPoint3 toReturn = location; + if (m_NavMesh != nullptr) { + float pos[3]; + pos[0] = location.x; + pos[1] = location.y; + pos[2] = location.z; + + dtPolyRef nearestRef = 0; + float polyPickExt[3] = { halfExtent, halfExtent, halfExtent }; + float nearestPoint[3] = { 0.0f, 0.0f, 0.0f }; + dtQueryFilter filter{}; + + auto hasPoly = m_NavQuery->findNearestPoly(pos, polyPickExt, &filter, &nearestRef, nearestPoint); + if (hasPoly != DT_SUCCESS) { + toReturn = location; + } else { + toReturn.x = nearestPoint[0]; + toReturn.y = nearestPoint[1]; + toReturn.z = nearestPoint[2]; + } + } + return toReturn; +} + float dNavMesh::GetHeightAtPoint(const NiPoint3& location, const float halfExtentsHeight) const { if (m_NavMesh == nullptr) { return location.y; diff --git a/dNavigation/dNavMesh.h b/dNavigation/dNavMesh.h index 8a55c649..60e07e7c 100644 --- a/dNavigation/dNavMesh.h +++ b/dNavigation/dNavMesh.h @@ -21,7 +21,7 @@ public: /** * Get the height at a point - * + * * @param location The location to check for height at. This is the center of the search area. * @param halfExtentsHeight The half extents height of the search area. This is the distance from the center to the top and bottom of the search area. * The larger the value of halfExtentsHeight is, the larger the performance cost of the query. @@ -29,7 +29,7 @@ public: */ float GetHeightAtPoint(const NiPoint3& location, const float halfExtentsHeight = 32.0f) const; std::vector GetPath(const NiPoint3& startPos, const NiPoint3& endPos, float speed = 10.0f); - + NiPoint3 NearestPoint(const NiPoint3& location, const float halfExtent = 32.0f) const; bool IsNavmeshLoaded() { return m_NavMesh != nullptr; } private: From ed00551982a76a5af8d90e27a653fc5902e72cdb Mon Sep 17 00:00:00 2001 From: TAHuntling <38479763+TAHuntling@users.noreply.github.com> Date: Tue, 21 May 2024 20:02:07 -0500 Subject: [PATCH 2/5] feat: Help Command Pagination (#1581) * Update SlashCommandHandler.cpp * Update SlashCommandHandler.cpp * Update SlashCommandHandler.cpp * Update SlashCommandHandler.cpp * Update SlashCommandHandler.cpp * Update SlashCommandHandler.cpp * Update SlashCommandHandler.cpp * Update SlashCommandHandler.cpp * Update SlashCommandHandler.cpp * Update SlashCommandHandler.cpp * Update SlashCommandHandler.cpp * Fixed Comments Now able to do /command help to see info for said command. Additionally this works for aliases. Fixed serialization missing from merge. * Update SlashCommandHandler.cpp * Update SlashCommandHandler.cpp * Update SlashCommandHandler.cpp * Update SlashCommandHandler.cpp * Update SlashCommandHandler.cpp * Update SlashCommandHandler.cpp * Update SlashCommandHandler.cpp * Update SlashCommandHandler.cpp --- dGame/dUtilities/SlashCommandHandler.cpp | 89 +++++++++++++++++------- 1 file changed, 65 insertions(+), 24 deletions(-) diff --git a/dGame/dUtilities/SlashCommandHandler.cpp b/dGame/dUtilities/SlashCommandHandler.cpp index 3ba1ab38..9eccc268 100644 --- a/dGame/dUtilities/SlashCommandHandler.cpp +++ b/dGame/dUtilities/SlashCommandHandler.cpp @@ -8,6 +8,7 @@ #include "SlashCommandHandler.h" #include +#include #include "DEVGMCommands.h" #include "GMGreaterThanZeroCommands.h" @@ -60,11 +61,9 @@ void SlashCommandHandler::HandleChatCommand(const std::u16string& chat, Entity* if (commandHandle.requiredLevel > eGameMasterLevel::CIVILIAN) Database::Get()->InsertSlashCommandUsage(entity->GetObjectID(), input); commandHandle.handle(entity, sysAddr, args); } else if (entity->GetGMLevel() != eGameMasterLevel::CIVILIAN) { - // We don't need to tell civilians they aren't high enough level error = "You are not high enough GM level to use \"" + command + "\""; } } else if (entity->GetGMLevel() == eGameMasterLevel::CIVILIAN) { - // We don't need to tell civilians commands don't exist error = "Command " + command + " does not exist!"; } @@ -75,32 +74,74 @@ void SlashCommandHandler::HandleChatCommand(const std::u16string& chat, Entity* void GMZeroCommands::Help(Entity* entity, const SystemAddress& sysAddr, const std::string args) { std::ostringstream feedback; - if (args.empty()) { - feedback << "----- Commands -----"; - for (const auto& [alias, command] : CommandInfos) { - // TODO: Limit displaying commands based on GM level they require - if (command.requiredLevel > entity->GetGMLevel()) continue; - LOG("Help command: %s", alias.c_str()); - feedback << "\n/" << alias << ": " << command.help; + constexpr size_t pageSize = 10; + + std::string trimmedArgs = args; + trimmedArgs.erase(trimmedArgs.begin(), std::find_if_not(trimmedArgs.begin(), trimmedArgs.end(), [](unsigned char ch) { + return std::isspace(ch); + })); + trimmedArgs.erase(std::find_if_not(trimmedArgs.rbegin(), trimmedArgs.rend(), [](unsigned char ch) { + return std::isspace(ch); + }).base(), trimmedArgs.end()); + + std::optional parsedPage = GeneralUtils::TryParse(trimmedArgs); + if (trimmedArgs.empty() || parsedPage.has_value()) { + size_t page = parsedPage.value_or(1); + + std::map accessibleCommands; + for (const auto& [commandName, command] : CommandInfos) { + if (command.requiredLevel <= entity->GetGMLevel()) { + accessibleCommands.emplace(commandName, command); + } + } + + size_t totalPages = (accessibleCommands.size() + pageSize - 1) / pageSize; + + if (page < 1 || page > totalPages) { + feedback << "Invalid page number. Total pages: " << totalPages; + GameMessages::SendSlashCommandFeedbackText(entity, GeneralUtils::ASCIIToUTF16(feedback.str())); + return; + } + + auto it = accessibleCommands.begin(); + std::advance(it, (page - 1) * pageSize); + size_t endIdx = std::min(page * pageSize, accessibleCommands.size()); + + feedback << "----- Commands (Page " << page << " of " << totalPages << ") -----"; + for (size_t i = (page - 1) * pageSize; i < endIdx; ++i, ++it) { + feedback << "\n/" << it->first << ": " << it->second.help; + } + + const auto feedbackStr = feedback.str(); + if (!feedbackStr.empty()) { + GameMessages::SendSlashCommandFeedbackText(entity, GeneralUtils::ASCIIToUTF16(feedbackStr)); + } + return; + } + + auto it = std::ranges::find_if(CommandInfos, [&trimmedArgs](const auto& pair) { + return std::ranges::find(pair.second.aliases, trimmedArgs) != pair.second.aliases.end(); + }); + + if (it != CommandInfos.end() && entity->GetGMLevel() >= it->second.requiredLevel) { + const auto& command = it->second; + feedback << "----- " << it->first << " Info -----\n"; + feedback << command.info << "\n"; + if (command.aliases.size() > 1) { + feedback << "Aliases: "; + for (size_t i = 0; i < command.aliases.size(); ++i) { + if (i > 0) feedback << ", "; + feedback << command.aliases[i]; + } } } else { - auto it = CommandInfos.find(args); - if (it != CommandInfos.end() && entity->GetGMLevel() >= it->second.requiredLevel) { - feedback << "----- " << args << " -----\n"; - feedback << it->second.info; - if (it->second.aliases.size() > 1) { - feedback << "\nAliases: "; - for (size_t i = 0; i < it->second.aliases.size(); i++) { - if (i > 0) feedback << ", "; - feedback << it->second.aliases[i]; - } - } - } else if (entity->GetGMLevel() > eGameMasterLevel::CIVILIAN) { - feedback << "Command " << std::quoted(args) << " does not exist!"; - } + feedback << "Command not found."; } + const auto feedbackStr = feedback.str(); - if (!feedbackStr.empty()) GameMessages::SendSlashCommandFeedbackText(entity, GeneralUtils::ASCIIToUTF16(feedbackStr)); + if (!feedbackStr.empty()) { + GameMessages::SendSlashCommandFeedbackText(entity, GeneralUtils::ASCIIToUTF16(feedbackStr)); + } } void SlashCommandHandler::SendAnnouncement(const std::string& title, const std::string& message) { From dea10c6d56dbb568df32b967843941af65b60182 Mon Sep 17 00:00:00 2001 From: TAHuntling <38479763+TAHuntling@users.noreply.github.com> Date: Wed, 22 May 2024 08:32:24 -0500 Subject: [PATCH 3/5] Client commands implementation (#1592) * Adding Client Commands Adding list of client commands provided to me by EmosewaMC * Finished adding client commands --- dGame/dUtilities/SlashCommandHandler.cpp | 379 ++++++++++++++++++ .../SlashCommands/GMZeroCommands.cpp | 4 + .../dUtilities/SlashCommands/GMZeroCommands.h | 1 + 3 files changed, 384 insertions(+) diff --git a/dGame/dUtilities/SlashCommandHandler.cpp b/dGame/dUtilities/SlashCommandHandler.cpp index 9eccc268..428ccbcb 100644 --- a/dGame/dUtilities/SlashCommandHandler.cpp +++ b/dGame/dUtilities/SlashCommandHandler.cpp @@ -1048,4 +1048,383 @@ void SlashCommandHandler::Startup() { }; RegisterCommand(InstanceInfoCommand); + //Commands that are handled by the client + + Command faqCommand{ + .help = "Show the LU FAQ Page", + .info = "Show the LU FAQ Page", + .aliases = {"faq","faqs"}, + .handle = GMZeroCommands::ClientHandled, + .requiredLevel = eGameMasterLevel::CIVILIAN + }; + RegisterCommand(faqCommand); + + Command teamChatCommand{ + .help = "Send a message to your teammates.", + .info = "Send a message to your teammates.", + .aliases = {"team","t"}, + .handle = GMZeroCommands::ClientHandled, + .requiredLevel = eGameMasterLevel::CIVILIAN + }; + RegisterCommand(teamChatCommand); + + Command showStoreCommand{ + .help = "Show the LEGO shop page.", + .info = "Show the LEGO shop page.", + .aliases = {"shop","store"}, + .handle = GMZeroCommands::ClientHandled, + .requiredLevel = eGameMasterLevel::CIVILIAN + }; + RegisterCommand(showStoreCommand); + + Command minigamesCommand{ + .help = "Show the LEGO minigames page!", + .info = "Show the LEGO minigames page!", + .aliases = {"minigames"}, + .handle = GMZeroCommands::ClientHandled, + .requiredLevel = eGameMasterLevel::CIVILIAN + }; + RegisterCommand(minigamesCommand); + + Command forumsCommand{ + .help = "Show the LU Forums!", + .info = "Show the LU Forums!", + .aliases = {"forums"}, + .handle = GMZeroCommands::ClientHandled, + .requiredLevel = eGameMasterLevel::CIVILIAN + }; + RegisterCommand(forumsCommand); + + Command exitGameCommand{ + .help = "Exit to desktop", + .info = "Exit to desktop", + .aliases = {"exit","quit"}, + .handle = GMZeroCommands::ClientHandled, + .requiredLevel = eGameMasterLevel::CIVILIAN + }; + RegisterCommand(exitGameCommand); + + Command thumbsUpCommand{ + .help = "Oh, yeah!", + .info = "Oh, yeah!", + .aliases = {"thumb","thumbs","thumbsup"}, + .handle = GMZeroCommands::ClientHandled, + .requiredLevel = eGameMasterLevel::CIVILIAN + }; + RegisterCommand(thumbsUpCommand); + + Command victoryCommand{ + .help = "Victory!", + .info = "Victory!", + .aliases = {"victory!"}, + .handle = GMZeroCommands::ClientHandled, + .requiredLevel = eGameMasterLevel::CIVILIAN + }; + RegisterCommand(victoryCommand); + + Command backflipCommand{ + .help = "Do a flip!", + .info = "Do a flip!", + .aliases = {"backflip"}, + .handle = GMZeroCommands::ClientHandled, + .requiredLevel = eGameMasterLevel::CIVILIAN + }; + RegisterCommand(backflipCommand); + + Command clapCommand{ + .help = "A round of applause!", + .info = "A round of applause!", + .aliases = {"clap"}, + .handle = GMZeroCommands::ClientHandled, + .requiredLevel = eGameMasterLevel::CIVILIAN + }; + RegisterCommand(clapCommand); + + Command logoutCharacterCommand{ + .help = "Returns you to the character select screen.", + .info = "Returns you to the character select screen.", + .aliases = {"camp","logoutcharacter"}, + .handle = GMZeroCommands::ClientHandled, + .requiredLevel = eGameMasterLevel::CIVILIAN + }; + RegisterCommand(logoutCharacterCommand); + + Command sayCommand{ + .help = "Say something outloud so that everyone can hear you", + .info = "Say something outloud so that everyone can hear you", + .aliases = {"s","say"}, + .handle = GMZeroCommands::ClientHandled, + .requiredLevel = eGameMasterLevel::CIVILIAN + }; + RegisterCommand(sayCommand); + + Command whisperCommand{ + .help = "Send a private message to another player.", + .info = "Send a private message to another player.", + .aliases = {"tell","w","whisper"}, + .handle = GMZeroCommands::ClientHandled, + .requiredLevel = eGameMasterLevel::CIVILIAN + }; + RegisterCommand(whisperCommand); + + Command locationCommand{ + .help = "Output your current location on the map to the chat box.", + .info = "Output your current location on the map to the chat box.", + .aliases = {"loc","locate","location"}, + .handle = GMZeroCommands::ClientHandled, + .requiredLevel = eGameMasterLevel::CIVILIAN + }; + RegisterCommand(locationCommand); + + Command logoutCommand{ + .help = "Returns you to the login screen.", + .info = "Returns you to the login screen.", + .aliases = {"logout","logoutaccount"}, + .handle = GMZeroCommands::ClientHandled, + .requiredLevel = eGameMasterLevel::CIVILIAN + }; + RegisterCommand(logoutCommand); + + Command shrugCommand{ + .help = "I dunno...", + .info = "I dunno...", + .aliases = {"shrug"}, + .handle = GMZeroCommands::ClientHandled, + .requiredLevel = eGameMasterLevel::CIVILIAN + }; + RegisterCommand(shrugCommand); + + Command leaveTeamCommand{ + .help = "Leave your current team.", + .info = "Leave your current team.", + .aliases = {"leave","leaveteam","teamleave","tleave"}, + .handle = GMZeroCommands::ClientHandled, + .requiredLevel = eGameMasterLevel::CIVILIAN + }; + RegisterCommand(leaveTeamCommand); + + Command teamLootTypeCommand{ + .help = "[rr|ffa] Set the loot for your current team (round-robin/free for all).", + .info = "[rr|ffa] Set the loot for your current team (round-robin/free for all).", + .aliases = {"setloot","teamsetloot","tloot","tsetloot"}, + .handle = GMZeroCommands::ClientHandled, + .requiredLevel = eGameMasterLevel::CIVILIAN + }; + RegisterCommand(teamLootTypeCommand); + + Command removeFriendCommand{ + .help = "[name] Removes a player from your friends list.", + .info = "[name] Removes a player from your friends list.", + .aliases = {"removefriend"}, + .handle = GMZeroCommands::ClientHandled, + .requiredLevel = eGameMasterLevel::CIVILIAN + }; + RegisterCommand(removeFriendCommand); + + Command yesCommand{ + .help = "Aye aye, captain!", + .info = "Aye aye, captain!", + .aliases = {"yes"}, + .handle = GMZeroCommands::ClientHandled, + .requiredLevel = eGameMasterLevel::CIVILIAN + }; + RegisterCommand(yesCommand); + + Command teamInviteCommand{ + .help = "[name] Invite a player to your team.", + .info = "[name] Invite a player to your team.", + .aliases = {"invite","inviteteam","teaminvite","tinvite"}, + .handle = GMZeroCommands::ClientHandled, + .requiredLevel = eGameMasterLevel::CIVILIAN + }; + RegisterCommand(teamInviteCommand); + + Command danceCommand{ + .help = "Dance 'til you can't dance no more.", + .info = "Dance 'til you can't dance no more.", + .aliases = {"dance"}, + .handle = GMZeroCommands::ClientHandled, + .requiredLevel = eGameMasterLevel::CIVILIAN + }; + RegisterCommand(danceCommand); + + Command sighCommand{ + .help = "Another day, another brick.", + .info = "Another day, another brick.", + .aliases = {"sigh"}, + .handle = GMZeroCommands::ClientHandled, + .requiredLevel = eGameMasterLevel::CIVILIAN + }; + RegisterCommand(sighCommand); + + Command recommendedOptionsCommand{ + .help = "Sets the recommended performance options in the cfg file", + .info = "Sets the recommended performance options in the cfg file", + .aliases = {"recommendedperfoptions"}, + .handle = GMZeroCommands::ClientHandled, + .requiredLevel = eGameMasterLevel::CIVILIAN + }; + RegisterCommand(recommendedOptionsCommand); + + Command setTeamLeaderCommand{ + .help = "[name] Set the leader for your current team.", + .info = "[name] Set the leader for your current team.", + .aliases = {"leader","setleader","teamsetleader","tleader","tsetleader"}, + .handle = GMZeroCommands::ClientHandled, + .requiredLevel = eGameMasterLevel::CIVILIAN + }; + RegisterCommand(setTeamLeaderCommand); + + Command cringeCommand{ + .help = "I don't even want to talk about it...", + .info = "I don't even want to talk about it...", + .aliases = {"cringe"}, + .handle = GMZeroCommands::ClientHandled, + .requiredLevel = eGameMasterLevel::CIVILIAN + }; + RegisterCommand(cringeCommand); + + Command talkCommand{ + .help = "Jibber Jabber", + .info = "Jibber Jabber", + .aliases = {"talk"}, + .handle = GMZeroCommands::ClientHandled, + .requiredLevel = eGameMasterLevel::CIVILIAN + }; + RegisterCommand(talkCommand); + + Command cancelQueueCommand{ + .help = "Cancel Your position in the queue if you are in one.", + .info = "Cancel Your position in the queue if you are in one.", + .aliases = {"cancelqueue"}, + .handle = GMZeroCommands::ClientHandled, + .requiredLevel = eGameMasterLevel::CIVILIAN + }; + RegisterCommand(cancelQueueCommand); + + Command lowPerformanceCommand{ + .help = "Sets the default low-spec performance options in the cfg file", + .info = "Sets the default low-spec performance options in the cfg file", + .aliases = {"perfoptionslow"}, + .handle = GMZeroCommands::ClientHandled, + .requiredLevel = eGameMasterLevel::CIVILIAN + }; + RegisterCommand(lowPerformanceCommand); + + Command kickFromTeamCommand{ + .help = "[name] Kick a player from your current team.", + .info = "[name] Kick a player from your current team.", + .aliases = {"kick","kickplayer","teamkickplayer","tkick","tkickplayer"}, + .handle = GMZeroCommands::ClientHandled, + .requiredLevel = eGameMasterLevel::CIVILIAN + }; + RegisterCommand(kickFromTeamCommand); + + Command thanksCommand{ + .help = "Express your gratitude for another.", + .info = "Express your gratitude for another.", + .aliases = {"thanks"}, + .handle = GMZeroCommands::ClientHandled, + .requiredLevel = eGameMasterLevel::CIVILIAN + }; + RegisterCommand(thanksCommand); + + Command waveCommand{ + .help = "Wave to other players.", + .info = "Wave to other players.", + .aliases = {"wave"}, + .handle = GMZeroCommands::ClientHandled, + .requiredLevel = eGameMasterLevel::CIVILIAN + }; + RegisterCommand(waveCommand); + + Command whyCommand{ + .help = "Why|!?!!", + .info = "Why|!?!!", + .aliases = {"why"}, + .handle = GMZeroCommands::ClientHandled, + .requiredLevel = eGameMasterLevel::CIVILIAN + }; + RegisterCommand(whyCommand); + + Command midPerformanceCommand{ + .help = "Sets the default medium-spec performance options in the cfg file", + .info = "Sets the default medium-spec performance options in the cfg file", + .aliases = {"perfoptionsmid"}, + .handle = GMZeroCommands::ClientHandled, + .requiredLevel = eGameMasterLevel::CIVILIAN + }; + RegisterCommand(midPerformanceCommand); + + Command highPerformanceCommand{ + .help = "Sets the default high-spec performance options in the cfg file", + .info = "Sets the default high-spec performance options in the cfg file", + .aliases = {"perfoptionshigh"}, + .handle = GMZeroCommands::ClientHandled, + .requiredLevel = eGameMasterLevel::CIVILIAN + }; + RegisterCommand(highPerformanceCommand); + + Command gaspCommand{ + .help = "Oh my goodness!", + .info = "Oh my goodness!", + .aliases = {"gasp"}, + .handle = GMZeroCommands::ClientHandled, + .requiredLevel = eGameMasterLevel::CIVILIAN + }; + RegisterCommand(gaspCommand); + + Command ignoreCommand{ + .help = "[name] Add a player to your ignore list.", + .info = "[name] Add a player to your ignore list.", + .aliases = {"addignore"}, + .handle = GMZeroCommands::ClientHandled, + .requiredLevel = eGameMasterLevel::CIVILIAN + }; + RegisterCommand(ignoreCommand); + + Command addFriendCommand{ + .help = "[name] Add a player to your friends list.", + .info = "[name] Add a player to your friends list.", + .aliases = {"addfriend"}, + .handle = GMZeroCommands::ClientHandled, + .requiredLevel = eGameMasterLevel::CIVILIAN + }; + RegisterCommand(addFriendCommand); + + Command cryCommand{ + .help = "Show everyone your 'Aw' face.", + .info = "Show everyone your 'Aw' face.", + .aliases = {"cry"}, + .handle = GMZeroCommands::ClientHandled, + .requiredLevel = eGameMasterLevel::CIVILIAN + }; + RegisterCommand(cryCommand); + + Command giggleCommand{ + .help = "A good little chuckle", + .info = "A good little chuckle", + .aliases = {"giggle"}, + .handle = GMZeroCommands::ClientHandled, + .requiredLevel = eGameMasterLevel::CIVILIAN + }; + RegisterCommand(giggleCommand); + + Command saluteCommand{ + .help = "For those about to build...", + .info = "For those about to build...", + .aliases = {"salute"}, + .handle = GMZeroCommands::ClientHandled, + .requiredLevel = eGameMasterLevel::CIVILIAN + }; + RegisterCommand(saluteCommand); + + Command removeIgnoreCommand{ + .help = "[name] Removes a player from your ignore list.", + .info = "[name] Removes a player from your ignore list.", + .aliases = {"removeIgnore"}, + .handle = GMZeroCommands::ClientHandled, + .requiredLevel = eGameMasterLevel::CIVILIAN + }; + RegisterCommand(removeIgnoreCommand); } diff --git a/dGame/dUtilities/SlashCommands/GMZeroCommands.cpp b/dGame/dUtilities/SlashCommands/GMZeroCommands.cpp index f183d5ea..6c9811c2 100644 --- a/dGame/dUtilities/SlashCommands/GMZeroCommands.cpp +++ b/dGame/dUtilities/SlashCommands/GMZeroCommands.cpp @@ -224,5 +224,9 @@ namespace GMZeroCommands { ChatPackets::SendSystemMessage(sysAddr, u"Map: " + (GeneralUtils::to_u16string(zoneId.GetMapID())) + u"\nClone: " + (GeneralUtils::to_u16string(zoneId.GetCloneID())) + u"\nInstance: " + (GeneralUtils::to_u16string(zoneId.GetInstanceID()))); } + + //For client side commands + void ClientHandled(Entity* entity, const SystemAddress& sysAddr, const std::string args) {} + }; diff --git a/dGame/dUtilities/SlashCommands/GMZeroCommands.h b/dGame/dUtilities/SlashCommands/GMZeroCommands.h index 3b2389b5..d3f6753d 100644 --- a/dGame/dUtilities/SlashCommands/GMZeroCommands.h +++ b/dGame/dUtilities/SlashCommands/GMZeroCommands.h @@ -15,6 +15,7 @@ namespace GMZeroCommands { void LeaveZone(Entity* entity, const SystemAddress& sysAddr, const std::string args); void Resurrect(Entity* entity, const SystemAddress& sysAddr, const std::string args); void InstanceInfo(Entity* entity, const SystemAddress& sysAddr, const std::string args); + void ClientHandled(Entity* entity, const SystemAddress& sysAddr, const std::string args); } #endif //!GMZEROCOMMANDS_H From dc430d975894e53f199282e75ff7c94d32151d09 Mon Sep 17 00:00:00 2001 From: David Markowitz <39972741+EmosewaMC@users.noreply.github.com> Date: Wed, 22 May 2024 16:35:45 -0700 Subject: [PATCH 4/5] Add reputation as a repeatable mission reward (#1590) This reverts commit 7d1a28b492b263aba2008a5984dc0f5e7348a068. Add stubbing for abbreviations Reward reputation always if possible --- dGame/dMission/Mission.cpp | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/dGame/dMission/Mission.cpp b/dGame/dMission/Mission.cpp index c2ed2a42..2a841e39 100644 --- a/dGame/dMission/Mission.cpp +++ b/dGame/dMission/Mission.cpp @@ -454,6 +454,16 @@ void Mission::YieldRewards() { } } + // Even with no repeatable column, reputation is repeatable + if (info.reward_reputation > 0) { + missionComponent->Progress(eMissionTaskType::EARN_REPUTATION, 0, LWOOBJID_EMPTY, "", info.reward_reputation); + auto* const character = entity->GetComponent(); + if (character) { + character->SetReputation(character->GetReputation() + info.reward_reputation); + GameMessages::SendUpdateReputation(entity->GetObjectID(), character->GetReputation(), entity->GetSystemAddress()); + } + } + if (m_Completions > 0) { std::vector> items; @@ -532,15 +542,6 @@ void Mission::YieldRewards() { modelInventory->SetSize(modelInventory->GetSize() + info.reward_bankinventory); } - if (info.reward_reputation > 0) { - missionComponent->Progress(eMissionTaskType::EARN_REPUTATION, 0, 0L, "", info.reward_reputation); - auto character = entity->GetComponent(); - if (character) { - character->SetReputation(character->GetReputation() + info.reward_reputation); - GameMessages::SendUpdateReputation(entity->GetObjectID(), character->GetReputation(), entity->GetSystemAddress()); - } - } - if (info.reward_maxhealth > 0) { destroyableComponent->SetMaxHealth(destroyableComponent->GetMaxHealth() + static_cast(info.reward_maxhealth), true); } From f0960d48b21d4d558ad378d6bcf9c7c0d6f8ba3d Mon Sep 17 00:00:00 2001 From: David Markowitz <39972741+EmosewaMC@users.noreply.github.com> Date: Wed, 22 May 2024 17:06:52 -0700 Subject: [PATCH 5/5] Add more modular saving of config data for items (#1591) * stubbing for saving item extra data * add declaration to header * modularize loading for all possible extra data * move logic to Item * remove extra map --- dCommon/LDFFormat.h | 74 ++++++++++++------------ dGame/dComponents/InventoryComponent.cpp | 41 ++++--------- dGame/dInventory/Item.cpp | 55 +++++++++++++++++- dGame/dInventory/Item.h | 14 +++++ 4 files changed, 115 insertions(+), 69 deletions(-) diff --git a/dCommon/LDFFormat.h b/dCommon/LDFFormat.h index 2cd9156c..054ddb42 100644 --- a/dCommon/LDFFormat.h +++ b/dCommon/LDFFormat.h @@ -31,22 +31,22 @@ public: virtual ~LDFBaseData() {} - virtual void WriteToPacket(RakNet::BitStream& packet) = 0; + virtual void WriteToPacket(RakNet::BitStream& packet) const = 0; - virtual const std::u16string& GetKey() = 0; + virtual const std::u16string& GetKey() const = 0; - virtual eLDFType GetValueType() = 0; + virtual eLDFType GetValueType() const = 0; /** Gets a string from the key/value pair * @param includeKey Whether or not to include the key in the data * @param includeTypeId Whether or not to include the type id in the data * @return The string representation of the data */ - virtual std::string GetString(bool includeKey = true, bool includeTypeId = true) = 0; + virtual std::string GetString(bool includeKey = true, bool includeTypeId = true) const = 0; - virtual std::string GetValueAsString() = 0; + virtual std::string GetValueAsString() const = 0; - virtual LDFBaseData* Copy() = 0; + virtual LDFBaseData* Copy() const = 0; /** * Given an input string, return the data as a LDF key. @@ -62,7 +62,7 @@ private: T value; //! Writes the key to the packet - void WriteKey(RakNet::BitStream& packet) { + void WriteKey(RakNet::BitStream& packet) const { packet.Write(this->key.length() * sizeof(uint16_t)); for (uint32_t i = 0; i < this->key.length(); ++i) { packet.Write(this->key[i]); @@ -70,7 +70,7 @@ private: } //! Writes the value to the packet - void WriteValue(RakNet::BitStream& packet) { + void WriteValue(RakNet::BitStream& packet) const { packet.Write(this->GetValueType()); packet.Write(this->value); } @@ -90,7 +90,7 @@ public: /*! \return The value */ - const T& GetValue(void) { return this->value; } + const T& GetValue(void) const { return this->value; } //! Sets the value /*! @@ -102,13 +102,13 @@ public: /*! \return The value string */ - std::string GetValueString(void) { return ""; } + std::string GetValueString(void) const { return ""; } //! Writes the data to a packet /*! \param packet The packet */ - void WriteToPacket(RakNet::BitStream& packet) override { + void WriteToPacket(RakNet::BitStream& packet) const override { this->WriteKey(packet); this->WriteValue(packet); } @@ -117,13 +117,13 @@ public: /*! \return The key */ - const std::u16string& GetKey(void) override { return this->key; } + const std::u16string& GetKey(void) const override { return this->key; } //! Gets the LDF Type /*! \return The LDF value type */ - eLDFType GetValueType(void) override { return LDF_TYPE_UNKNOWN; } + eLDFType GetValueType(void) const override { return LDF_TYPE_UNKNOWN; } //! Gets the string data /*! @@ -131,7 +131,7 @@ public: \param includeTypeId Whether or not to include the type id in the data \return The string representation of the data */ - std::string GetString(const bool includeKey = true, const bool includeTypeId = true) override { + std::string GetString(const bool includeKey = true, const bool includeTypeId = true) const override { if (GetValueType() == -1) { return GeneralUtils::UTF16ToWTF8(this->key) + "=-1:"; } @@ -154,11 +154,11 @@ public: return stream.str(); } - std::string GetValueAsString() override { + std::string GetValueAsString() const override { return this->GetValueString(); } - LDFBaseData* Copy() override { + LDFBaseData* Copy() const override { return new LDFData(key, value); } @@ -166,19 +166,19 @@ public: }; // LDF Types -template<> inline eLDFType LDFData::GetValueType(void) { return LDF_TYPE_UTF_16; }; -template<> inline eLDFType LDFData::GetValueType(void) { return LDF_TYPE_S32; }; -template<> inline eLDFType LDFData::GetValueType(void) { return LDF_TYPE_FLOAT; }; -template<> inline eLDFType LDFData::GetValueType(void) { return LDF_TYPE_DOUBLE; }; -template<> inline eLDFType LDFData::GetValueType(void) { return LDF_TYPE_U32; }; -template<> inline eLDFType LDFData::GetValueType(void) { return LDF_TYPE_BOOLEAN; }; -template<> inline eLDFType LDFData::GetValueType(void) { return LDF_TYPE_U64; }; -template<> inline eLDFType LDFData::GetValueType(void) { return LDF_TYPE_OBJID; }; -template<> inline eLDFType LDFData::GetValueType(void) { return LDF_TYPE_UTF_8; }; +template<> inline eLDFType LDFData::GetValueType(void) const { return LDF_TYPE_UTF_16; }; +template<> inline eLDFType LDFData::GetValueType(void) const { return LDF_TYPE_S32; }; +template<> inline eLDFType LDFData::GetValueType(void) const { return LDF_TYPE_FLOAT; }; +template<> inline eLDFType LDFData::GetValueType(void) const { return LDF_TYPE_DOUBLE; }; +template<> inline eLDFType LDFData::GetValueType(void) const { return LDF_TYPE_U32; }; +template<> inline eLDFType LDFData::GetValueType(void) const { return LDF_TYPE_BOOLEAN; }; +template<> inline eLDFType LDFData::GetValueType(void) const { return LDF_TYPE_U64; }; +template<> inline eLDFType LDFData::GetValueType(void) const { return LDF_TYPE_OBJID; }; +template<> inline eLDFType LDFData::GetValueType(void) const { return LDF_TYPE_UTF_8; }; // The specialized version for std::u16string (UTF-16) template<> -inline void LDFData::WriteValue(RakNet::BitStream& packet) { +inline void LDFData::WriteValue(RakNet::BitStream& packet) const { packet.Write(this->GetValueType()); packet.Write(this->value.length()); @@ -189,7 +189,7 @@ inline void LDFData::WriteValue(RakNet::BitStream& packet) { // The specialized version for bool template<> -inline void LDFData::WriteValue(RakNet::BitStream& packet) { +inline void LDFData::WriteValue(RakNet::BitStream& packet) const { packet.Write(this->GetValueType()); packet.Write(this->value); @@ -197,7 +197,7 @@ inline void LDFData::WriteValue(RakNet::BitStream& packet) { // The specialized version for std::string (UTF-8) template<> -inline void LDFData::WriteValue(RakNet::BitStream& packet) { +inline void LDFData::WriteValue(RakNet::BitStream& packet) const { packet.Write(this->GetValueType()); packet.Write(this->value.length()); @@ -206,18 +206,18 @@ inline void LDFData::WriteValue(RakNet::BitStream& packet) { } } -template<> inline std::string LDFData::GetValueString() { +template<> inline std::string LDFData::GetValueString() const { return GeneralUtils::UTF16ToWTF8(this->value, this->value.size()); } -template<> inline std::string LDFData::GetValueString() { return std::to_string(this->value); } -template<> inline std::string LDFData::GetValueString() { return std::to_string(this->value); } -template<> inline std::string LDFData::GetValueString() { return std::to_string(this->value); } -template<> inline std::string LDFData::GetValueString() { return std::to_string(this->value); } -template<> inline std::string LDFData::GetValueString() { return std::to_string(this->value); } -template<> inline std::string LDFData::GetValueString() { return std::to_string(this->value); } -template<> inline std::string LDFData::GetValueString() { return std::to_string(this->value); } +template<> inline std::string LDFData::GetValueString() const { return std::to_string(this->value); } +template<> inline std::string LDFData::GetValueString() const { return std::to_string(this->value); } +template<> inline std::string LDFData::GetValueString() const { return std::to_string(this->value); } +template<> inline std::string LDFData::GetValueString() const { return std::to_string(this->value); } +template<> inline std::string LDFData::GetValueString() const { return std::to_string(this->value); } +template<> inline std::string LDFData::GetValueString() const { return std::to_string(this->value); } +template<> inline std::string LDFData::GetValueString() const { return std::to_string(this->value); } -template<> inline std::string LDFData::GetValueString() { return this->value; } +template<> inline std::string LDFData::GetValueString() const { return this->value; } #endif //!__LDFFORMAT__H__ diff --git a/dGame/dComponents/InventoryComponent.cpp b/dGame/dComponents/InventoryComponent.cpp index 60dd071c..acb27796 100644 --- a/dGame/dComponents/InventoryComponent.cpp +++ b/dGame/dComponents/InventoryComponent.cpp @@ -558,19 +558,9 @@ void InventoryComponent::LoadXml(const tinyxml2::XMLDocument& document) { itemElement->QueryAttribute("parent", &parent); // End custom xml - std::vector config; + auto* item = new Item(id, lot, inventory, slot, count, bound, {}, parent, subKey); - auto* extraInfo = itemElement->FirstChildElement("x"); - - if (extraInfo) { - std::string modInfo = extraInfo->Attribute("ma"); - - LDFBaseData* moduleAssembly = new LDFData(u"assemblyPartLOTs", GeneralUtils::ASCIIToUTF16(modInfo.substr(2, modInfo.size() - 1))); - - config.push_back(moduleAssembly); - } - - const auto* item = new Item(id, lot, inventory, slot, count, bound, config, parent, subKey); + item->LoadConfigXml(*itemElement); if (equipped) { const auto info = Inventory::FindItemComponent(lot); @@ -676,17 +666,7 @@ void InventoryComponent::UpdateXml(tinyxml2::XMLDocument& document) { itemElement->SetAttribute("parent", item->GetParent()); // End custom xml - for (auto* data : item->GetConfig()) { - if (data->GetKey() != u"assemblyPartLOTs") { - continue; - } - - auto* extraInfo = document.NewElement("x"); - - extraInfo->SetAttribute("ma", data->GetString(false).c_str()); - - itemElement->LinkEndChild(extraInfo); - } + item->SaveConfigXml(*itemElement); bagElement->LinkEndChild(itemElement); } @@ -1600,18 +1580,18 @@ void InventoryComponent::UpdatePetXml(tinyxml2::XMLDocument& document) { } -bool InventoryComponent::SetSkill(int32_t slot, uint32_t skillId){ +bool InventoryComponent::SetSkill(int32_t slot, uint32_t skillId) { BehaviorSlot behaviorSlot = BehaviorSlot::Invalid; - if (slot == 1 ) behaviorSlot = BehaviorSlot::Primary; - else if (slot == 2 ) behaviorSlot = BehaviorSlot::Offhand; - else if (slot == 3 ) behaviorSlot = BehaviorSlot::Neck; - else if (slot == 4 ) behaviorSlot = BehaviorSlot::Head; - else if (slot == 5 ) behaviorSlot = BehaviorSlot::Consumable; + if (slot == 1) behaviorSlot = BehaviorSlot::Primary; + else if (slot == 2) behaviorSlot = BehaviorSlot::Offhand; + else if (slot == 3) behaviorSlot = BehaviorSlot::Neck; + else if (slot == 4) behaviorSlot = BehaviorSlot::Head; + else if (slot == 5) behaviorSlot = BehaviorSlot::Consumable; else return false; return SetSkill(behaviorSlot, skillId); } -bool InventoryComponent::SetSkill(BehaviorSlot slot, uint32_t skillId){ +bool InventoryComponent::SetSkill(BehaviorSlot slot, uint32_t skillId) { if (skillId == 0) return false; const auto index = m_Skills.find(slot); if (index != m_Skills.end()) { @@ -1623,4 +1603,3 @@ bool InventoryComponent::SetSkill(BehaviorSlot slot, uint32_t skillId){ m_Skills.insert_or_assign(slot, skillId); return true; } - diff --git a/dGame/dInventory/Item.cpp b/dGame/dInventory/Item.cpp index b6193692..32603761 100644 --- a/dGame/dInventory/Item.cpp +++ b/dGame/dInventory/Item.cpp @@ -27,6 +27,23 @@ #include "CDComponentsRegistryTable.h" #include "CDPackageComponentTable.h" +namespace { + const std::map ExtraSettingAbbreviations = { + { "assemblyPartLOTs", "ma" }, + { "blueprintID", "b" }, + { "userModelID", "ui" }, + { "userModelName", "un" }, + { "userModelDesc", "ud" }, + { "userModelHasBhvr", "ub" }, + { "userModelBehaviors", "ubh" }, + { "userModelBehaviorSourceID", "ubs" }, + { "userModelPhysicsType", "up" }, + { "userModelMod", "um" }, + { "userModelOpt", "uo" }, + { "reforgedLOT", "rl" }, + }; +} + Item::Item(const LWOOBJID id, const LOT lot, Inventory* inventory, const uint32_t slot, const uint32_t count, const bool bound, const std::vector& config, const LWOOBJID parent, LWOOBJID subKey, eLootSourceType lootSourceType) { if (!Inventory::IsValidItem(lot)) { return; @@ -122,6 +139,10 @@ uint32_t Item::GetSlot() const { return slot; } +std::vector Item::GetConfig() const { + return config; +} + std::vector& Item::GetConfig() { return config; } @@ -251,7 +272,7 @@ bool Item::Consume() { auto skills = skillsTable->Query([this](const CDObjectSkills entry) { return entry.objectTemplate == static_cast(lot); - }); + }); auto success = false; @@ -515,3 +536,35 @@ Item::~Item() { config.clear(); } + +void Item::SaveConfigXml(tinyxml2::XMLElement& i) const { + tinyxml2::XMLElement* x = nullptr; + + for (const auto* config : this->config) { + const auto& key = GeneralUtils::UTF16ToWTF8(config->GetKey()); + const auto saveKey = ExtraSettingAbbreviations.find(key); + if (saveKey == ExtraSettingAbbreviations.end()) { + continue; + } + + if (!x) { + x = i.InsertNewChildElement("x"); + } + + const auto dataToSave = config->GetString(false); + x->SetAttribute(saveKey->second.c_str(), dataToSave.c_str()); + } +} + +void Item::LoadConfigXml(const tinyxml2::XMLElement& i) { + const auto* x = i.FirstChildElement("x"); + if (!x) return; + + for (const auto& pair : ExtraSettingAbbreviations) { + const auto* data = x->Attribute(pair.second.c_str()); + if (!data) continue; + + const auto value = pair.first + "=" + data; + config.push_back(LDFBaseData::DataFromString(value)); + } +} diff --git a/dGame/dInventory/Item.h b/dGame/dInventory/Item.h index 04d05d7c..72ff264c 100644 --- a/dGame/dInventory/Item.h +++ b/dGame/dInventory/Item.h @@ -9,6 +9,10 @@ #include "eInventoryType.h" #include "eLootSourceType.h" +namespace tinyxml2 { + class XMLElement; +}; + /** * An item that can be stored in an inventory and optionally consumed or equipped * TODO: ideally this should be a component @@ -116,6 +120,12 @@ public: */ std::vector& GetConfig(); + /** + * Returns current config info for this item, e.g. for rockets + * @return current config info for this item + */ + std::vector GetConfig() const; + /** * Returns the database info for this item * @return the database info for this item @@ -214,6 +224,10 @@ public: */ void RemoveFromInventory(); + void SaveConfigXml(tinyxml2::XMLElement& i) const; + + void LoadConfigXml(const tinyxml2::XMLElement& i); + private: /** * The object ID of this item