/* * This file is part of the AzerothCore Project. See AUTHORS file for Copyright information * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU Affero General Public License as published by the * Free Software Foundation; either version 3 of the License, or (at your * option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for * more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . */ /** * @file cs_deserter.cpp * @brief .deserter related commands * * This file contains the CommandScripts for all deserter sub-commands */ #include "Chat.h" #include "CommandScript.h" #include "Language.h" #include "Player.h" #include "SpellAuras.h" using namespace Acore::ChatCommands; enum Spells { LFG_SPELL_DUNGEON_DESERTER = 71041, BG_SPELL_DESERTER = 26013 }; class deserter_commandscript : public CommandScript { public: deserter_commandscript() : CommandScript("deserter_commandscript") { } /** * @brief Returns the command structure for the system. */ ChatCommandTable GetCommands() const override { static ChatCommandTable deserterInstanceCommandTable = { { "add", HandleDeserterInstanceAdd, SEC_ADMINISTRATOR, Console::Yes }, { "remove all", HandleDeserterInstanceRemoveAll, SEC_ADMINISTRATOR, Console::Yes }, { "remove", HandleDeserterInstanceRemove, SEC_ADMINISTRATOR, Console::Yes } }; static ChatCommandTable deserterBGCommandTable = { { "add", HandleDeserterBGAdd, SEC_ADMINISTRATOR, Console::Yes }, { "remove all", HandleDeserterBGRemoveAll, SEC_ADMINISTRATOR, Console::Yes }, { "remove", HandleDeserterBGRemove, SEC_ADMINISTRATOR, Console::Yes } }; static ChatCommandTable deserterCommandTable = { { "instance", deserterInstanceCommandTable }, { "bg", deserterBGCommandTable } }; static ChatCommandTable commandTable = { { "deserter", deserterCommandTable } }; return commandTable; } /** * @brief Applies the Deserter Debuff to a player * * This function applies a Deserter Debuff of the given type (Instance or BG) to the * selected player, with the provided duration in seconds. * * @param handler The ChatHandler, passed by the system. * @param playerName Player by name. Optional, defaults to selected or self. * @param time The provided duration as TimeString. Optional, defaults to bg/instance default time. * @param isInstance provided by the relaying functions, so we don't have * to write that much code :) * * @return true if everything was correct, false if an error occured. * * Example Usage: * @code * .deserter instance add 1h30m (using player target or self) * -or- * .deserter bg add 1h30m (using player target or self) * -or- * .deserter bg add Tester 1h30m (using player of name 'Tester') * @endcode */ static bool HandleDeserterAdd(ChatHandler* handler, Optional playerName, Optional time, bool isInstance) { Player* target = handler->getSelectedPlayerOrSelf(); ObjectGuid guid; if (playerName) { if (!normalizePlayerName(*playerName)) { handler->SendErrorMessage(LANG_PLAYER_NOT_FOUND); return false; } guid = sCharacterCache->GetCharacterGuidByName(*playerName); if (guid) { target = ObjectAccessor::FindPlayerByName(*playerName); } else { if (time) { handler->SendErrorMessage(LANG_PLAYER_NOT_FOUND); return false; } time = playerName; playerName = ""; } } if (!playerName || playerName->empty()) { if (!handler->GetSession()) { return false; } playerName = target->GetName(); guid = target->GetGUID(); } if (!time) { time = isInstance ? "30m" : "15m"; } int32 duration = TimeStringToSecs(*time); if (duration == 0) { duration = Acore::StringTo(*time).value_or(0); } if (duration == 0) { handler->SendErrorMessage(LANG_BAD_VALUE); return false; } uint32 deserterSpell = isInstance ? LFG_SPELL_DUNGEON_DESERTER : BG_SPELL_DESERTER; if (target) { Aura* aura = target->GetAura(deserterSpell); if (aura && aura->GetDuration() >= duration * IN_MILLISECONDS) { handler->PSendSysMessage("Player {} already has a longer {} Deserter active.", handler->playerLink(*playerName), isInstance ? "Instance" : "Battleground"); return true; } aura = target->AddAura(deserterSpell, target); if (!aura) { handler->SendErrorMessage(LANG_BAD_VALUE); return false; } aura->SetDuration(duration * IN_MILLISECONDS); } else { int32 remainTime = 0; if (QueryResult result = CharacterDatabase.Query("SELECT remainTime FROM character_aura WHERE guid = {} AND spell = {}", guid.GetCounter(), deserterSpell)) { Field* fields = result->Fetch(); remainTime = fields[0].Get(); if (remainTime < 0 || remainTime >= duration * IN_MILLISECONDS) { handler->PSendSysMessage("Player {} already has a longer {} Deserter active.", handler->playerLink(*playerName), isInstance ? "Instance" : "Battleground"); return true; } CharacterDatabase.Query("DELETE FROM character_aura WHERE guid = {} AND spell = {}", guid.GetCounter(), deserterSpell); } uint8 index = 0; CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_AURA); stmt->SetData(index++, guid.GetCounter()); stmt->SetData(index++, guid.GetCounter()); stmt->SetData(index++, 0); stmt->SetData(index++, deserterSpell); stmt->SetData(index++, 1); stmt->SetData(index++, 1); stmt->SetData(index++, 1); stmt->SetData(index++, 0); stmt->SetData(index++, 0); stmt->SetData(index++, 0); stmt->SetData(index++, 0); stmt->SetData(index++, 0); stmt->SetData(index++, 0); stmt->SetData(index++, isInstance ? 1800000 : 900000); stmt->SetData(index++, duration * IN_MILLISECONDS); stmt->SetData(index, 0); CharacterDatabase.Execute(stmt); } handler->PSendSysMessage("{} of {} Deserter has been added to player {}.", secsToTimeString(duration), isInstance ? "Instance" : "Battleground", handler->playerLink(*playerName)); return true; } /** * @brief Removes the Deserter Debuff from a player * * This function removes a Deserter Debuff of the given type (Instance or BG) from the * selected player. * * @param handler The ChatHandler, passed by the system. * @param player The target player, either by name, the target or self * @param isInstance provided by the relaying functions, so we don't have * to write that much code :) * * @return true if everything was correct, false if an error occured. * * Example Usage: * @code * .deserter instance remove (using player target or self) * -or- * .deserter bg remove (using player target or self) * -or- * .deserter bg remove Tester (using player of name 'Tester') * @endcode */ static bool HandleDeserterRemove(ChatHandler* handler, Optional player, bool isInstance) { if (!player) { player = PlayerIdentifier::FromTargetOrSelf(handler); } if (!player) { handler->SendErrorMessage(LANG_NO_CHAR_SELECTED); return false; } Player* target = player->GetConnectedPlayer(); uint32 deserterSpell = isInstance ? LFG_SPELL_DUNGEON_DESERTER : BG_SPELL_DESERTER; int32 duration = 0; if (target) { if (Aura* aura = target->GetAura(deserterSpell)) { duration = aura->GetDuration(); target->RemoveAura(deserterSpell); } } else { if (QueryResult result = CharacterDatabase.Query("SELECT remainTime FROM character_aura WHERE guid = {} AND spell = {}", player->GetGUID().GetCounter(), deserterSpell)) { Field* fields = result->Fetch(); duration = fields[0].Get(); CharacterDatabase.Execute("DELETE FROM character_aura WHERE guid = {} AND spell = {}", player->GetGUID().GetCounter(), deserterSpell); } } if (duration == 0) { handler->SendErrorMessage("Player {} does not have {} Deserter.", handler->playerLink(player->GetName()), isInstance ? "Instance" : "Battleground"); return true; } if (duration < 0) { handler->SendErrorMessage("Permanent {} Deserter has been removed from player {} (GUID {}).", isInstance ? "Instance" : "Battleground", handler->playerLink(player->GetName()), player->GetGUID().ToString()); return true; } handler->PSendSysMessage("{} of {} Deserter has been removed from player {} (GUID {}).", secsToTimeString(duration / IN_MILLISECONDS), isInstance ? "Instance" : "Battleground", handler->playerLink(player->GetName()), player->GetGUID().ToString()); return true; } /** * @brief Removes the Deserter Debuff from all players * * This function removes a Deserter Debuff of the given type (Instance or BG) from * all players, online or offline. * * @param handler The ChatHandler, passed by the system. * @param isInstance provided by the relaying functions, so we don't have * to write that much code :) * @param maxTime Optional: The maximum remaining time of the Debuff on players to be removed. * Any Player with a Deserter Debuff of this time or less will get their Debuff removed. Use -1 for any. * Default: 15m for BG, 30m for Instance. * * @return true if everything was correct, false if an error occured. * * Example Usage: * @code * .deserter bg remove all * -or- * .deserter bg remove all 30m * -or- * .deserter bg remove all -1 * @endcode */ static bool HandleDeserterRemoveAll(ChatHandler* handler, bool isInstance, Optional maxTime) { uint32 deserterSpell = isInstance ? LFG_SPELL_DUNGEON_DESERTER : BG_SPELL_DESERTER; int32 remainTime = isInstance ? 1800 : 1800; uint64 deserterCount = 0; bool countOnline = true; if (maxTime) { remainTime = TimeStringToSecs(*maxTime); if (remainTime == 0) { remainTime = Acore::StringTo(*maxTime).value_or(0); } } // Optimization. Do not execute any further functions or Queries if remainTime is 0. if (remainTime == 0) { handler->SendErrorMessage(LANG_BAD_VALUE); return false; } QueryResult result; if (remainTime > 0) { result = CharacterDatabase.Query("SELECT COUNT(guid) FROM character_aura WHERE spell = {} AND remainTime <= {}", deserterSpell, remainTime * IN_MILLISECONDS); } else { result = CharacterDatabase.Query("SELECT COUNT(guid) FROM character_aura WHERE spell = {}", deserterSpell); } if (result) { deserterCount = (*result)[0].Get(); } // Optimization. Only execute these if there even is a result. if (deserterCount > 0) { countOnline = false; if (remainTime > 0) { CharacterDatabase.Execute("DELETE FROM character_aura WHERE spell = {} AND remainTime <= {}", deserterSpell, remainTime * IN_MILLISECONDS); } else { CharacterDatabase.Execute("DELETE FROM character_aura WHERE spell = {}", deserterSpell); } } std::shared_lock lock(*HashMapHolder::GetLock()); HashMapHolder::MapType const& onlinePlayerList = ObjectAccessor::GetPlayers(); for (HashMapHolder::MapType::const_iterator itr = onlinePlayerList.begin(); itr != onlinePlayerList.end(); ++itr) { Player* player = itr->second; Aura* aura = player->GetAura(deserterSpell); if (aura && (remainTime < 0 || aura->GetDuration() <= remainTime * IN_MILLISECONDS)) { if (countOnline) deserterCount++; player->RemoveAura(deserterSpell); } } std::string remainTimeStr = secsToTimeString(remainTime); if (remainTime < 0) { remainTimeStr = "infinity"; } if (deserterCount == 0) { handler->PSendSysMessage("No player on this realm has {} Deserter with a duration of {} or less.", isInstance ? "Instance" : "Battleground", remainTimeStr); return true; } handler->PSendSysMessage("{} Deserter has been removed from {} player(s) with a duration of {} or less.", isInstance ? "Instance" : "Battleground", deserterCount, remainTimeStr); return true; } /// @sa HandleDeserterAdd() static bool HandleDeserterInstanceAdd(ChatHandler* handler, Optional playerName, Optional time) { return HandleDeserterAdd(handler, playerName, time, true); } /// @sa HandleDeserterAdd() static bool HandleDeserterBGAdd(ChatHandler* handler, Optional playerName, Optional time) { return HandleDeserterAdd(handler, playerName, time, false); } /// @sa HandleDeserterRemove() static bool HandleDeserterInstanceRemove(ChatHandler* handler, Optional player) { return HandleDeserterRemove(handler, player, true); } /// @sa HandleDeserterRemove() static bool HandleDeserterBGRemove(ChatHandler* handler, Optional player) { return HandleDeserterRemove(handler, player, false); } static bool HandleDeserterInstanceRemoveAll(ChatHandler* handler, Optional maxTime) { return HandleDeserterRemoveAll(handler, true, maxTime); } static bool HandleDeserterBGRemoveAll(ChatHandler* handler, Optional maxTime) { return HandleDeserterRemoveAll(handler, false, maxTime); } }; void AddSC_deserter_commandscript() { new deserter_commandscript(); }