diff options
Diffstat (limited to 'src/progs/manaverse/actions')
-rw-r--r-- | src/progs/manaverse/actions/actions.cpp | 2048 | ||||
-rw-r--r-- | src/progs/manaverse/actions/chat.cpp | 840 | ||||
-rw-r--r-- | src/progs/manaverse/actions/commands.cpp | 2225 | ||||
-rw-r--r-- | src/progs/manaverse/actions/move.cpp | 281 | ||||
-rw-r--r-- | src/progs/manaverse/actions/pets.cpp | 250 | ||||
-rw-r--r-- | src/progs/manaverse/actions/statusbar.cpp | 205 | ||||
-rw-r--r-- | src/progs/manaverse/actions/tabs.cpp | 107 | ||||
-rw-r--r-- | src/progs/manaverse/actions/target.cpp | 91 | ||||
-rw-r--r-- | src/progs/manaverse/actions/windows.cpp | 393 |
9 files changed, 6440 insertions, 0 deletions
diff --git a/src/progs/manaverse/actions/actions.cpp b/src/progs/manaverse/actions/actions.cpp new file mode 100644 index 000000000..8e997cb2a --- /dev/null +++ b/src/progs/manaverse/actions/actions.cpp @@ -0,0 +1,2048 @@ +/* + * The ManaPlus Client + * Copyright (C) 2012-2020 The ManaPlus Developers + * Copyright (C) 2020-2023 The ManaVerse Developers + * + * This file is part of The ManaPlus Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * 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 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 <http://www.gnu.org/licenses/>. + */ + +#include "actions/actions.h" + +#include "actormanager.h" +#include "configuration.h" +#include "game.h" +#ifdef USE_OPENGL +#include "graphicsmanager.h" +#endif // USE_OPENGL +#include "main.h" +#include "spellmanager.h" + +#include "actions/actiondef.h" + +#include "being/localplayer.h" +#include "being/playerinfo.h" + +#include "const/spells.h" + +#include "const/resources/skill.h" + +#include "fs/files.h" + +#include "gui/gui.h" +#include "gui/popupmanager.h" +#include "gui/sdlinput.h" +#include "gui/windowmanager.h" + +#include "gui/shortcut/dropshortcut.h" +#include "gui/shortcut/emoteshortcut.h" +#include "gui/shortcut/itemshortcut.h" + +#include "gui/popups/popupmenu.h" + +#include "gui/windows/buydialog.h" +#include "gui/windows/okdialog.h" +#include "gui/windows/tradewindow.h" +#include "gui/windows/quitdialog.h" +#include "gui/windows/buyselldialog.h" +#include "gui/windows/chatwindow.h" +#include "gui/windows/helpwindow.h" +#include "gui/windows/inventorywindow.h" +#include "gui/windows/itemamountwindow.h" +#include "gui/windows/npcdialog.h" +#include "gui/windows/outfitwindow.h" +#include "gui/windows/setupwindow.h" +#include "gui/windows/shopwindow.h" +#include "gui/windows/shortcutwindow.h" +#include "gui/windows/skilldialog.h" +#include "gui/windows/whoisonline.h" + +#include "gui/widgets/createwidget.h" + +#include "gui/widgets/tabs/chat/chattab.h" + +#include "input/inputactionoperators.h" + +#if defined USE_OPENGL +#include "render/normalopenglgraphics.h" +#endif // USE_OPENGL + +#include "net/adminhandler.h" +#include "net/beinghandler.h" +#include "net/buyingstorehandler.h" +#include "net/buysellhandler.h" +#include "net/chathandler.h" +#include "net/download.h" +#include "net/homunculushandler.h" +#include "net/gamehandler.h" +#include "net/inventoryhandler.h" +#include "net/ipc.h" +#include "net/mercenaryhandler.h" +#include "net/net.h" +#include "net/npchandler.h" +#include "net/serverfeatures.h" +#include "net/uploadcharinfo.h" +#include "net/tradehandler.h" +#include "net/vendinghandler.h" + +#include "resources/iteminfo.h" +#include "resources/memorymanager.h" + +#include "resources/resourcemanager/resourcemanager.h" + +#include "utils/chatutils.h" +#include "utils/foreach.h" +#include "utils/gettext.h" +#include "utils/parameters.h" +#include "utils/timer.h" + +#ifdef TMWA_SUPPORT +#include "net/playerhandler.h" + +#include "utils/mathutils.h" +#endif // TMWA_SUPPORT + +PRAGMA48(GCC diagnostic push) +PRAGMA48(GCC diagnostic ignored "-Wshadow") +#ifdef ANDROID +#ifndef USE_SDL2 +#include <SDL_screenkeyboard.h> +#endif // USE_OPENGL +#endif // ANDROID +PRAGMA48(GCC diagnostic pop) + +#include <sstream> + +#include "debug.h" + +extern std::string tradePartnerName; +extern QuitDialog *quitDialog; +extern time_t start_time; +extern char **environ; + +namespace Actions +{ + +static int uploadUpdate(void *ptr, + const DownloadStatusT status, + size_t total A_UNUSED, + const size_t remaining A_UNUSED) A_NONNULL(1); + +static int uploadUpdate(void *ptr, + const DownloadStatusT status, + size_t total A_UNUSED, + const size_t remaining A_UNUSED) +{ + if (status == DownloadStatus::Idle || status == DownloadStatus::Starting) + return 0; + + UploadChatInfo *const info = reinterpret_cast<UploadChatInfo*>(ptr); + if (info == nullptr) + return 0; + + if (status == DownloadStatus::Complete) + { + std::string str = Net::Download::getUploadResponse(); + const size_t sz = str.size(); + if (sz > 0) + { + if (str[sz - 1] == '\n') + str = str.substr(0, sz - 1); + str.append(info->addStr); + ChatTab *const tab = info->tab; + if (chatWindow != nullptr && + (tab == nullptr || chatWindow->isTabPresent(tab))) + { + str = strprintf("%s [@@%s |%s@@]", + info->text.c_str(), str.c_str(), str.c_str()); + outStringNormal(tab, str, str); + } + else + { + CREATEWIDGET(OkDialog, + // TRANSLATORS: file uploaded message + _("File uploaded"), + str, + // TRANSLATORS: ok dialog button + _("OK"), + DialogType::OK, + Modal_true, + ShowCenter_false, + nullptr, + 260); + } + } + } +// delete2(info->upload) + info->upload = nullptr; + delete info; + return 0; +} + +static void uploadFile(const std::string &str, + const std::string &fileName, + const std::string &addStr, + ChatTab *const tab) +{ + UploadChatInfo *const info = new UploadChatInfo; + Net::Download *const upload = new Net::Download(info, + "http://ix.io", + &uploadUpdate, + false, true, false); + info->upload = upload; + info->text = str; + info->addStr = addStr; + info->tab = tab; + upload->setFile(fileName, -1); + upload->start(); +} + +static Being *findBeing(const std::string &name, const bool npc) +{ + if ((localPlayer == nullptr) || (actorManager == nullptr)) + return nullptr; + + Being *being = nullptr; + + if (name.empty()) + { + being = localPlayer->getTarget(); + } + else + { + being = actorManager->findBeingByName( + name, ActorType::Unknown); + } + if ((being == nullptr) && npc) + { + being = actorManager->findNearestLivingBeing( + localPlayer, 1, ActorType::Npc, AllowSort_true); + if (being != nullptr) + { + if (abs(being->getTileX() - localPlayer->getTileX()) > 1 + || abs(being->getTileY() - localPlayer->getTileY()) > 1) + { + being = nullptr; + } + } + } + if ((being == nullptr) && npc) + { + being = actorManager->findNearestLivingBeing( + localPlayer, 1, ActorType::Player, AllowSort_true); + if (being != nullptr) + { + if (abs(being->getTileX() - localPlayer->getTileX()) > 1 + || abs(being->getTileY() - localPlayer->getTileY()) > 1) + { + being = nullptr; + } + } + } + return being; +} + +static Item *getItemByInvIndex(const int index, + const InventoryTypeT invType) +{ + const Inventory *inv = nullptr; + switch (invType) + { + case InventoryType::Storage: + inv = PlayerInfo::getStorageInventory(); + break; + + case InventoryType::Inventory: + inv = PlayerInfo::getInventory(); + break; + case InventoryType::Trade: + case InventoryType::Npc: + case InventoryType::Cart: + case InventoryType::Vending: + case InventoryType::MailEdit: + case InventoryType::MailView: + case InventoryType::Craft: + case InventoryType::TypeEnd: + default: + break; + } + if (inv != nullptr) + return inv->getItem(index); + return nullptr; +} + +static int getAmountFromEvent(const InputEvent &event, + Item *&item0, + const InventoryTypeT invType) +{ + Item *const item = getItemByInvIndex(atoi(event.args.c_str()), + invType); + item0 = item; + if (item == nullptr) + return 0; + + std::string str = event.args; + removeToken(str, " "); + + if (str.empty()) + return 0; + + int amount = 0; + if (str[0] == '-') + { + if (str.size() > 1) + { + amount = item->getQuantity() - atoi(str.substr(1).c_str()); + if (amount <= 0 || amount > item->getQuantity()) + amount = item->getQuantity(); + } + } + else if (str == "/") + { + amount = item->getQuantity() / 2; + } + else if (str == "all") + { + amount = item->getQuantity(); + } + else + { + amount = atoi(str.c_str()); + } + return amount; +} + +impHandler(emote) +{ + const int emotion = 1 + (event.action - InputAction::EMOTE_1); + if (emotion > 0) + { + if (emoteShortcut != nullptr) + emoteShortcut->useEmotePlayer(emotion); + if (Game::instance() != nullptr) + Game::instance()->setValidSpeed(); + return true; + } + + return false; +} + +impHandler(outfit) +{ + if (inputManager.isActionActive(InputAction::WEAR_OUTFIT)) + { + const int num = event.action - InputAction::OUTFIT_1; + if ((outfitWindow != nullptr) && num >= 0) + { + outfitWindow->wearOutfit(num, + true, + false); + if (Game::instance() != nullptr) + Game::instance()->setValidSpeed(); + return true; + } + } + else if (inputManager.isActionActive(InputAction::COPY_OUTFIT)) + { + const int num = event.action - InputAction::OUTFIT_1; + if ((outfitWindow != nullptr) && num >= 0) + { + outfitWindow->copyOutfit(num); + if (Game::instance() != nullptr) + Game::instance()->setValidSpeed(); + return true; + } + } + + return false; +} + +impHandler0(mouseClick) +{ + if ((guiInput == nullptr) || (gui == nullptr)) + return false; + + int mouseX; + int mouseY; + Gui::getMouseState(mouseX, mouseY); + guiInput->simulateMouseClick(mouseX, mouseY, MouseButton::RIGHT); + return true; +} + +impHandler0(ok) +{ + // Close the Browser if opened + if ((helpWindow != nullptr) && helpWindow->isWindowVisible()) + { + helpWindow->setVisible(Visible_false); + return true; + } + // Close the config window, cancelling changes if opened + else if ((setupWindow != nullptr) && setupWindow->isWindowVisible()) + { + setupWindow->action(ActionEvent(nullptr, "cancel")); + return true; + } + else if (NpcDialog *const dialog = NpcDialog::getActive()) + { + dialog->action(ActionEvent(nullptr, "ok")); + return true; + } + else if (popupMenu->isPopupVisible()) + { + popupMenu->select(); + } + return false; +} + +impHandler(shortcut) +{ + if (itemShortcutWindow != nullptr) + { + const int num = itemShortcutWindow->getTabIndex(); + if (num >= 0 && num < CAST_S32(SHORTCUT_TABS)) + { + if (itemShortcut[num] != nullptr) + { + itemShortcut[num]->useItem(event.action + - InputAction::SHORTCUT_1); + } + } + return true; + } + return false; +} + +impHandler0(quit) +{ + if (Game::instance() == nullptr) + return false; + if (PopupManager::isPopupMenuVisible()) + { + PopupManager::closePopupMenu(); + return true; + } + else if (quitDialog == nullptr) + { + CREATEWIDGETV(quitDialog, QuitDialog, + &quitDialog); + quitDialog->requestMoveToTop(); + return true; + } + return false; +} + +impHandler0(dropItem0) +{ + if (dropShortcut != nullptr) + { + dropShortcut->dropFirst(); + return true; + } + return false; +} + +impHandler0(dropItem) +{ + if (dropShortcut != nullptr) + { + dropShortcut->dropItems(1); + return true; + } + return false; +} + +impHandler(dropItemId) +{ + const Inventory *const inv = PlayerInfo::getInventory(); + if (inv == nullptr) + return false; + + // +++ ignoring item color for now + Item *const item = inv->findItem(atoi(event.args.c_str()), + ItemColor_one); + + if ((item != nullptr) && !PlayerInfo::isItemProtected(item->getId())) + { + ItemAmountWindow::showWindow(ItemAmountWindowUsage::ItemDrop, + inventoryWindow, + item, + 0, + 0); + } + return true; +} + +impHandler(dropItemInv) +{ + Item *const item = getItemByInvIndex(atoi(event.args.c_str()), + InventoryType::Inventory); + if ((item != nullptr) && !PlayerInfo::isItemProtected(item->getId())) + { + ItemAmountWindow::showWindow(ItemAmountWindowUsage::ItemDrop, + inventoryWindow, + item, + 0, + 0); + } + return true; +} + +impHandler(dropItemIdAll) +{ + const Inventory *const inv = PlayerInfo::getInventory(); + if (inv == nullptr) + return false; + + // +++ ignoring item color for now + Item *const item = inv->findItem(atoi(event.args.c_str()), + ItemColor_one); + + if ((item != nullptr) && !PlayerInfo::isItemProtected(item->getId())) + PlayerInfo::dropItem(item, item->getQuantity(), Sfx_true); + return true; +} + +impHandler(dropItemInvAll) +{ + Item *const item = getItemByInvIndex(atoi(event.args.c_str()), + InventoryType::Inventory); + if ((item != nullptr) && !PlayerInfo::isItemProtected(item->getId())) + PlayerInfo::dropItem(item, item->getQuantity(), Sfx_true); + return true; +} + +#ifdef TMWA_SUPPORT +impHandler(heal) +{ + if (Net::getNetworkType() != ServerType::TMWATHENA) + return false; + if (actorManager != nullptr && + localPlayer != nullptr) + { + std::string args = event.args; + + if (!args.empty()) + { + const Being *being = nullptr; + if (args[0] == ':') + { + being = actorManager->findBeing(fromInt(atoi( + args.substr(1).c_str()), BeingId)); + if (being != nullptr && being->getType() == ActorType::Monster) + being = nullptr; + } + else + { + being = actorManager->findBeingByName(args, ActorType::Player); + } + if (being != nullptr) + actorManager->heal(being); + } + else + { + Being *target = localPlayer->getTarget(); + if (inputManager.isActionActive(InputAction::STOP_ATTACK)) + { + if (target == nullptr || + target->getType() != ActorType::Player) + { + target = actorManager->findNearestLivingBeing( + localPlayer, 10, ActorType::Player, AllowSort_true); + } + } + else + { + if (target == nullptr) + target = localPlayer; + } + actorManager->heal(target); + } + + if (Game::instance() != nullptr) + Game::instance()->setValidSpeed(); + return true; + } + return false; +} +#else // TMWA_SUPPORT + +impHandler0(heal) +{ + return false; +} +#endif // TMWA_SUPPORT + +impHandler0(healmd) +{ +#ifdef TMWA_SUPPORT + if (Net::getNetworkType() != ServerType::TMWATHENA) + return false; + if (actorManager != nullptr) + { + const int matk = PlayerInfo::getStatEffective(Attributes::PLAYER_MATK); + int maxHealingRadius; + + // magic levels < 2 + if (PlayerInfo::getSkillLevel(340) < 2 + || PlayerInfo::getSkillLevel(341) < 2) + { + maxHealingRadius = matk / 100 + 1; + } + else + { + maxHealingRadius = (12 * fastSqrtInt(matk) + matk) / 100 + 1; + } + + Being *target = actorManager->findMostDamagedPlayer(maxHealingRadius); + if (target != nullptr) + actorManager->heal(target); + + if (Game::instance() != nullptr) + Game::instance()->setValidSpeed(); + return true; + } +#endif // TMWA_SUPPORT + + return false; +} + +impHandler0(itenplz) +{ +#ifdef TMWA_SUPPORT + if (Net::getNetworkType() != ServerType::TMWATHENA) + return false; + if (actorManager != nullptr) + { + if (playerHandler != nullptr && + playerHandler->canUseMagic() && + PlayerInfo::getAttribute(Attributes::PLAYER_MP) >= 3) + { + actorManager->itenplz(); + } + return true; + } +#endif // TMWA_SUPPORT + + return false; +} + +impHandler0(setHome) +{ + if (localPlayer != nullptr) + { + localPlayer->setHome(); + return true; + } + return false; +} + +impHandler0(magicAttack) +{ +#ifdef TMWA_SUPPORT + if (Net::getNetworkType() != ServerType::TMWATHENA) + return false; + if (localPlayer != nullptr) + { + localPlayer->magicAttack(); + return true; + } +#endif // TMWA_SUPPORT + + return false; +} + +impHandler0(copyEquippedToOutfit) +{ + if (outfitWindow != nullptr) + { + outfitWindow->copyFromEquiped(); + return true; + } + return false; +} + +impHandler(pickup) +{ + if (localPlayer == nullptr) + return false; + + const std::string args = event.args; + if (args.empty()) + { + localPlayer->pickUpItems(0); + } + else + { + FloorItem *const item = actorManager->findItem(fromInt( + atoi(args.c_str()), BeingId)); + if (item != nullptr) + localPlayer->pickUp(item); + } + return true; +} + +static void doSit() +{ + if (inputManager.isActionActive(InputAction::EMOTE)) + localPlayer->updateSit(); + else + localPlayer->toggleSit(); +} + +impHandler0(sit) +{ + if (localPlayer != nullptr) + { + doSit(); + return true; + } + return false; +} + +impHandler(screenshot) +{ + Game::createScreenshot(event.args); + return true; +} + +impHandler0(ignoreInput) +{ + return true; +} + +impHandler(buy) +{ + if (serverFeatures == nullptr) + return false; + const std::string args = event.args; + Being *being = findBeing(args, false); + if ((being == nullptr) && Net::getNetworkType() == ServerType::TMWATHENA) + { + if (whoIsOnline != nullptr) + { + const std::set<std::string> &players = + whoIsOnline->getOnlineNicks(); + if (players.find(args) != players.end()) + { + if (buySellHandler != nullptr) + buySellHandler->requestSellList(args); + return true; + } + } + return false; + } + + if (being == nullptr) + being = findBeing(args, true); + + if (being == nullptr) + return false; + + if (being->getType() == ActorType::Npc) + { + if (npcHandler != nullptr) + npcHandler->buy(being); + return true; + } + else if (being->getType() == ActorType::Player) + { + if (vendingHandler != nullptr && + Net::getNetworkType() != ServerType::TMWATHENA) + { + vendingHandler->open(being); + } + else if (buySellHandler != nullptr) + { + buySellHandler->requestSellList(being->getName()); + } + return true; + } + return false; +} + +impHandler(sell) +{ + if (serverFeatures == nullptr) + return false; + + const std::string args = event.args; + Being *being = findBeing(args, false); + if (being == nullptr && + Net::getNetworkType() == ServerType::TMWATHENA) + { + if (whoIsOnline != nullptr) + { + const std::set<std::string> &players = + whoIsOnline->getOnlineNicks(); + if (players.find(args) != players.end()) + { + if (buySellHandler != nullptr) + buySellHandler->requestBuyList(args); + return true; + } + } + return false; + } + + if (being == nullptr) + being = findBeing(args, true); + + if (being == nullptr) + return false; + + if (being->getType() == ActorType::Npc) + { + if (npcHandler != nullptr) + npcHandler->sell(being->getId()); + return true; + } + else if (being->getType() == ActorType::Player) + { + if ((buyingStoreHandler != nullptr) && + Net::getNetworkType() != ServerType::TMWATHENA) + { + buyingStoreHandler->open(being); + } + else if (buySellHandler != nullptr) + { + buySellHandler->requestBuyList(being->getName()); + } + return true; + } + return false; +} + +impHandler(talk) +{ + const std::string args = event.args; + Being *being = nullptr; + + if (!args.empty() && args[0] == ':') + { + being = actorManager->findBeing(fromInt(atoi( + args.substr(1).c_str()), BeingId)); + } + else + { + being = findBeing(args, true); + } + + if (being == nullptr) + return false; + + if (being->canTalk()) + { + being->talkTo(); + } + else if (being->getType() == ActorType::Player) + { + CREATEWIDGET(BuySellDialog, + being->getName()); + } + return true; +} + +impHandler0(stopAttack) +{ + if (localPlayer != nullptr) + { + localPlayer->stopAttack(false); + // not consume if target attack key pressed + if (inputManager.isActionActive(InputAction::TARGET_ATTACK)) + return false; + return true; + } + return false; +} + +impHandler0(untarget) +{ + if (localPlayer != nullptr) + { + localPlayer->untarget(); + return true; + } + return false; +} + +impHandler(attack) +{ + if ((localPlayer == nullptr) || (actorManager == nullptr)) + return false; + + Being *target = nullptr; + std::string args = event.args; + if (!args.empty()) + { + if (args[0] != ':') + { + target = actorManager->findNearestByName(args, + ActorType::Unknown); + } + else + { + target = actorManager->findBeing(fromInt(atoi( + args.substr(1).c_str()), BeingId)); + if (target != nullptr && + target->getType() != ActorType::Monster) + { + target = nullptr; + } + } + } + if (target == nullptr) + target = localPlayer->getTarget(); + else + localPlayer->setTarget(target); + if (target != nullptr) + localPlayer->attack(target, true, false); + return true; +} + +impHandler(targetAttack) +{ + if ((localPlayer != nullptr) && (actorManager != nullptr)) + { + Being *target = nullptr; + std::string args = event.args; + const bool newTarget = !inputManager.isActionActive( + InputAction::STOP_ATTACK); + + if (!args.empty()) + { + if (args[0] != ':') + { + target = actorManager->findNearestByName(args, + ActorType::Unknown); + } + else + { + target = actorManager->findBeing(fromInt(atoi( + args.substr(1).c_str()), BeingId)); + if (target != nullptr && + target->getType() != ActorType::Monster) + { + target = nullptr; + } + } + } + + if ((target == nullptr) && (settings.targetingType == 0U)) + target = localPlayer->getTarget(); + + if (target == nullptr) + { + target = actorManager->findNearestLivingBeing( + localPlayer, 90, ActorType::Monster, AllowSort_true); + } + + localPlayer->attack2(target, newTarget, false); + return true; + } + return false; +} + +impHandler0(attackHuman) +{ + if ((actorManager == nullptr) || (localPlayer == nullptr)) + return false; + + Being *const target = actorManager->findNearestPvpPlayer(); + if (target != nullptr) + { + localPlayer->setTarget(target); + localPlayer->attack2(target, true, false); + } + return true; +} + +impHandler0(safeVideoMode) +{ + WindowManager::setFullScreen(false); + + return true; +} + +impHandler0(stopSit) +{ + if (localPlayer != nullptr) + { + localPlayer->stopAttack(false); + // not consume if target attack key pressed + if (inputManager.isActionActive(InputAction::TARGET_ATTACK)) + return false; + if (localPlayer->getTarget() == nullptr) + { + doSit(); + return true; + } + return true; + } + return false; +} + +impHandler0(showKeyboard) +{ +#if defined(ANDROID) || defined(__SWITCH__) +#ifdef USE_SDL2 + if (SDL_GetEventState(SDL_TEXTINPUT) == SDL_ENABLE) + SDL_StopTextInput(); + else + SDL_StartTextInput(); +#else // USE_SDL2 + + SDL_ANDROID_ToggleScreenKeyboardTextInput(nullptr); +#endif // USE_SDL2 + + return true; +#else // ANDROID + + return false; +#endif // ANDROID +} + +impHandler0(showWindows) +{ + if (popupMenu != nullptr) + { + popupMenu->showWindowsPopup(); + return true; + } + return false; +} + +impHandler0(openTrade) +{ + const Being *const being = localPlayer->getTarget(); + if ((being != nullptr) && being->getType() == ActorType::Player) + { + if (tradeHandler != nullptr) + tradeHandler->request(being); + tradePartnerName = being->getName(); + if (tradeWindow != nullptr) + tradeWindow->clear(); + return true; + } + return false; +} + +impHandler0(ipcToggle) +{ + if (ipc != nullptr) + { + IPC::stop(); + if (ipc == nullptr) + { + debugChatTab->chatLog("IPC service stopped.", + ChatMsgType::BY_SERVER, + IgnoreRecord_false, + TryRemoveColors_true); + } + else + { + debugChatTab->chatLog("Unable to stop IPC service.", + ChatMsgType::BY_SERVER, + IgnoreRecord_false, + TryRemoveColors_true); + } + } + else + { + IPC::start(); + if (ipc != nullptr) + { + debugChatTab->chatLog( + strprintf("IPC service available on port %d", ipc->getPort()), + ChatMsgType::BY_SERVER, + IgnoreRecord_false, + TryRemoveColors_true); + } + else + { + debugChatTab->chatLog("Unable to start IPC service", + ChatMsgType::BY_SERVER, + IgnoreRecord_false, + TryRemoveColors_true); + } + } + return true; +} + +impHandler(where) +{ + ChatTab *const tab = event.tab != nullptr ? event.tab : debugChatTab; + if (tab == nullptr) + return false; + std::ostringstream where; + where << Game::instance()->getCurrentMapName() << ", coordinates: " + << ((localPlayer->getPixelX() - mapTileSize / 2) / mapTileSize) + << ", " << ((localPlayer->getPixelY() - mapTileSize) / mapTileSize); + tab->chatLog(where.str(), + ChatMsgType::BY_SERVER, + IgnoreRecord_false, + TryRemoveColors_true); + return true; +} + +impHandler0(who) +{ + if (chatHandler != nullptr) + chatHandler->who(); + return true; +} + +impHandler0(cleanGraphics) +{ + ResourceManager::clearCache(); + + if (debugChatTab != nullptr) + { + // TRANSLATORS: clear graphics command message + debugChatTab->chatLog(_("Cache cleared"), + ChatMsgType::BY_SERVER, + IgnoreRecord_false, + TryRemoveColors_true); + } + return true; +} + +impHandler0(cleanFonts) +{ + if (gui != nullptr) + gui->clearFonts(); + if (debugChatTab != nullptr) + { + // TRANSLATORS: clear fonts cache message + debugChatTab->chatLog(_("Cache cleared"), + ChatMsgType::BY_SERVER, + IgnoreRecord_false, + TryRemoveColors_true); + } + return true; +} + +impHandler(trade) +{ + if (actorManager == nullptr) + return false; + + const Being *being = actorManager->findBeingByName( + event.args, ActorType::Player); + if (being == nullptr) + being = localPlayer->getTarget(); + if (being != nullptr) + { + if (tradeHandler != nullptr) + tradeHandler->request(being); + tradePartnerName = being->getName(); + if (tradeWindow != nullptr) + tradeWindow->clear(); + } + return true; +} + +impHandler0(priceLoad) +{ + if (shopWindow != nullptr) + { + shopWindow->loadList(); + return true; + } + return false; +} + +impHandler0(priceSave) +{ + if (shopWindow != nullptr) + { + shopWindow->saveList(); + return true; + } + return false; +} + +impHandler0(cacheInfo) +{ + if ((chatWindow == nullptr) || (debugChatTab == nullptr)) + return false; + +/* + Font *const font = chatWindow->getFont(); + if (!font) + return; + + const TextChunkList *const cache = font->getCache(); + if (!cache) + return; + + unsigned int all = 0; + // TRANSLATORS: chat fonts message + debugChatTab->chatLog(_("font cache size"), + ChatMsgType::BY_SERVER, + IgnoreRecord_false, + TryRemoveColors_true); + std::string str; + for (int f = 0; f < 256; f ++) + { + if (!cache[f].size) + { + const unsigned int sz = CAST_S32(cache[f].size); + all += sz; + str.append(strprintf("%d: %u, ", f, sz)); + } + } + debugChatTab->chatLog(str, + ChatMsgType::BY_SERVER, + IgnoreRecord_false, + TryRemoveColors_true); + // TRANSLATORS: chat fonts message + debugChatTab->chatLog(strprintf("%s %d", _("Cache size:"), all), + ChatMsgType::BY_SERVER, + IgnoreRecord_false, + TryRemoveColors_true); +#ifdef DEBUG_FONT_COUNTERS + debugChatTab->chatLog("", + ChatMsgType::BY_SERVER, + IgnoreRecord_false, + TryRemoveColors_true); + debugChatTab->chatLog(strprintf("%s %d", + // TRANSLATORS: chat fonts message + _("Created:"), font->getCreateCounter()), + ChatMsgType::BY_SERVER, + IgnoreRecord_false, + TryRemoveColors_true); + debugChatTab->chatLog(strprintf("%s %d", + // TRANSLATORS: chat fonts message + _("Deleted:"), font->getDeleteCounter()), + ChatMsgType::BY_SERVER, + IgnoreRecord_false, + TryRemoveColors_true); +#endif +*/ + return true; +} + +impHandler0(disconnect) +{ + if (!settings.options.uniqueSession) + { + if (gameHandler != nullptr) + gameHandler->disconnect2(); + return true; + } + else + return false; +} + +impHandler(undress) +{ + if ((actorManager == nullptr) || (localPlayer == nullptr)) + return false; + + const std::string args = event.args; + StringVect pars; + if (!splitParameters(pars, args, " ,", '\"')) + return false; + Being *target = nullptr; + const size_t sz = pars.size(); + if (sz == 0) + { + target = localPlayer->getTarget(); + } + else + { + if (pars[0][0] == ':') + { + target = actorManager->findBeing(fromInt(atoi( + pars[0].substr(1).c_str()), BeingId)); + if ((target != nullptr) && target->getType() == ActorType::Monster) + target = nullptr; + } + else + { + target = actorManager->findNearestByName(args, + ActorType::Unknown); + } + } + + if (sz == 2) + { + if (target != nullptr) + { + const int itemId = atoi(pars[1].c_str()); + target->undressItemById(itemId); + } + } + else + { + if ((target != nullptr) && (beingHandler != nullptr)) + beingHandler->undress(target); + } + + return true; +} + +impHandler0(dirs) +{ + if (debugChatTab == nullptr) + return false; + + debugChatTab->chatLog("config directory: " + + settings.configDir, + ChatMsgType::BY_SERVER, + IgnoreRecord_false, + TryRemoveColors_true); + debugChatTab->chatLog("logs directory: " + + settings.localDataDir, + ChatMsgType::BY_SERVER, + IgnoreRecord_false, + TryRemoveColors_true); + debugChatTab->chatLog("screenshots directory: " + + settings.screenshotDir, + ChatMsgType::BY_SERVER, + IgnoreRecord_false, + TryRemoveColors_true); + debugChatTab->chatLog("temp directory: " + + settings.tempDir, + ChatMsgType::BY_SERVER, + IgnoreRecord_false, + TryRemoveColors_true); + return true; +} + +impHandler0(uptime) +{ + if (debugChatTab == nullptr) + return false; + + if (cur_time < start_time) + { + // TRANSLATORS: uptime command + debugChatTab->chatLog(strprintf(_("Client uptime: %s"), "unknown"), + ChatMsgType::BY_SERVER, + IgnoreRecord_false, + TryRemoveColors_true); + } + else + { + // TRANSLATORS: uptime command + debugChatTab->chatLog(strprintf(_("Client uptime: %s"), + timeDiffToString(CAST_S32(cur_time - start_time)).c_str()), + ChatMsgType::BY_SERVER, + IgnoreRecord_false, + TryRemoveColors_true); + } + return true; +} + +#ifdef DEBUG_DUMP_LEAKS1 +static void showRes(std::string str, ResourceManager::Resources *res) +{ + if (!res) + return; + + str.append(toString(res->size())); + if (debugChatTab) + { + debugChatTab->chatLog(str, + ChatMsgType::BY_SERVER, + IgnoreRecord_false, + TryRemoveColors_true); + } + logger->log(str); + ResourceManager::ResourceIterator iter = res->begin(); + const ResourceManager::ResourceIterator iter_end = res->end(); + while (iter != iter_end) + { + if (iter->second && iter->second->mRefCount) + { + char type = ' '; + char isNew = 'N'; + if (iter->second->getDumped()) + isNew = 'O'; + else + iter->second->setDumped(true); + + SubImage *const subImage = dynamic_cast<SubImage *>( + iter->second); + Image *const image = dynamic_cast<Image *>(iter->second); + int id = 0; + if (subImage) + type = 'S'; + else if (image) + type = 'I'; + if (image) + id = image->getGLImage(); + logger->log("Resource %c%c: %s (%d) id=%d", type, + isNew, iter->second->getIdPath().c_str(), + iter->second->mRefCount, id); + } + ++ iter; + } +} + +impHandler(dump) +{ + if (!debugChatTab) + return false; + + if (!event.args.empty()) + { + ResourceManager::Resources *res = ResourceManager::getResources(); + // TRANSLATORS: dump command + showRes(_("Resource images:"), res); + res = ResourceManager::getOrphanedResources(); + // TRANSLATORS: dump command + showRes(_("Orphaned resource images:"), res); + } + else + { + ResourceManager::Resources *res = ResourceManager::getResources(); + // TRANSLATORS: dump command + debugChatTab->chatLog(_("Resource images:") + toString(res->size()), + ChatMsgType::BY_SERVER, + IgnoreRecord_false, + TryRemoveColors_true); + res = ResourceManager::getOrphanedResources(); + // TRANSLATORS: dump command + debugChatTab->chatLog(_("Orphaned resource images:") + + toString(res->size()), + ChatMsgType::BY_SERVER, + IgnoreRecord_false, + TryRemoveColors_true); + } + return true; +} + +#elif defined ENABLE_MEM_DEBUG +impHandler0(dump) +{ + nvwa::check_leaks(); + return true; +} +#else // DEBUG_DUMP_LEAKS1 + +impHandler0(dump) +{ + return true; +} +#endif // DEBUG_DUMP_LEAKS1 + +impHandler0(serverIgnoreAll) +{ + if (chatHandler != nullptr) + chatHandler->ignoreAll(); + return true; +} + +impHandler0(serverUnIgnoreAll) +{ + if (chatHandler != nullptr) + chatHandler->unIgnoreAll(); + return true; +} + +PRAGMA6(GCC diagnostic push) +PRAGMA6(GCC diagnostic ignored "-Wnull-dereference") +PRAGMA11(GCC diagnostic push) +PRAGMA11(GCC diagnostic ignored "-Warray-bounds") +impHandler0(error) +{ + int *const ptr = nullptr; + *(ptr + 1) = 20; +// logger->log("test %d", *ptr); + exit(1); +} +PRAGMA11(GCC diagnostic pop) +PRAGMA6(GCC diagnostic pop) + +impHandler(dumpGraphics) +{ + std::string str = strprintf("%s,%s,%dX%dX%d,", PACKAGE_OS, SMALL_VERSION, + mainGraphics->getWidth(), mainGraphics->getHeight(), + mainGraphics->getBpp()); + + if (mainGraphics->getFullScreen()) + str.append("F"); + else + str.append("W"); + if (mainGraphics->getHWAccel()) + str.append("H"); + else + str.append("S"); + + if (mainGraphics->getDoubleBuffer()) + str.append("D"); + else + str.append("_"); + +#if defined USE_OPENGL + str.append(strprintf(",%d", mainGraphics->getOpenGL())); +#else // defined USE_OPENGL + + str.append(",0"); +#endif // defined USE_OPENGL + + str.append(strprintf(",%f,", static_cast<double>(settings.guiAlpha))) + .append(config.getBoolValue("adjustPerfomance") ? "1" : "0") + .append(config.getBoolValue("alphaCache") ? "1" : "0") + .append(config.getBoolValue("enableMapReduce") ? "1" : "0") + .append(config.getBoolValue("beingopacity") ? "1" : "0") + .append(",") + .append(config.getBoolValue("enableAlphaFix") ? "1" : "0") + .append(config.getBoolValue("disableAdvBeingCaching") ? "1" : "0") + .append(config.getBoolValue("disableBeingCaching") ? "1" : "0") + .append(config.getBoolValue("particleeffects") ? "1" : "0") + .append(strprintf(",%d-%d", fps, config.getIntValue("fpslimit"))); + outStringNormal(event.tab, str, str); + return true; +} + +impHandler0(dumpEnvironment) +{ + logger->log1("Start environment variables"); + for (char **env = environ; *env != nullptr; ++ env) + logger->log1(*env); + logger->log1("End environment variables"); + if (debugChatTab != nullptr) + { + // TRANSLATORS: dump environment command + debugChatTab->chatLog(_("Environment variables dumped"), + ChatMsgType::BY_SERVER, + IgnoreRecord_false, + TryRemoveColors_true); + } + return true; +} + +impHandler(dumpTests) +{ + const std::string str = config.getStringValue("testInfo"); + outStringNormal(event.tab, str, str); + return true; +} + +impHandler0(dumpOGL) +{ +#ifdef USE_OPENGL +#if !defined(ANDROID) && !defined(__native_client__) && !defined(__SWITCH__) + NormalOpenGLGraphics::dumpSettings(); +#endif // !defined(ANDROID) && !defined(__native_client__) && + // !defined(__SWITCH__) +#endif // USE_OPENGL + + return true; +} + +#ifdef USE_OPENGL +impHandler(dumpGL) +{ + std::string str = graphicsManager.getGLVersion(); + outStringNormal(event.tab, str, str); + return true; +} +#else // USE_OPENGL + +impHandler0(dumpGL) +{ + return true; +} +#endif // USE_OPENGL + +impHandler(dumpMods) +{ + std::string str = "enabled mods: " + serverConfig.getValue("mods", ""); + outStringNormal(event.tab, str, str); + return true; +} + +#if defined USE_OPENGL && defined DEBUG_SDLFONT +impHandler0(testSdlFont) +{ + Font *font = new Font("fonts/dejavusans.ttf", 18, TTF_STYLE_NORMAL); + timespec time1; + timespec time2; + NullOpenGLGraphics *nullGraphics = new NullOpenGLGraphics; + STD_VECTOR<std::string> data; + volatile int width = 0; + + for (int f = 0; f < 300; f ++) + data.push_back("test " + toString(f) + "string"); + nullGraphics->beginDraw(); + + clock_gettime(CLOCK_MONOTONIC, &time1); + Color color(0, 0, 0, 255); + + for (int f = 0; f < 500; f ++) + { + FOR_EACH (STD_VECTOR<std::string>::const_iterator, it, data) + { + width += font->getWidth(*it); + font->drawString(nullGraphics, color, color, *it, 10, 10); + } + FOR_EACH (STD_VECTOR<std::string>::const_iterator, it, data) + font->drawString(nullGraphics, color, color, *it, 10, 10); + + font->doClean(); + } + + clock_gettime(CLOCK_MONOTONIC, &time2); + + delete nullGraphics; + delete font; + + int64_t diff = (static_cast<long long int>( + time2.tv_sec) * 1000000000LL + static_cast<long long int>( + time2.tv_nsec)) / 100000 - (static_cast<long long int>( + time1.tv_sec) * 1000000000LL + static_cast<long long int>( + time1.tv_nsec)) / 100000; + if (debugChatTab) + { + debugChatTab->chatLog("sdlfont time: " + toString(diff), + ChatMsgType::BY_SERVER, + IgnoreRecord_false, + TryRemoveColors_true); + } + return true; +} +#endif // defined USE_OPENGL && defined DEBUG_SDLFONT + +impHandler0(createItems) +{ + BuyDialog *const dialog = CREATEWIDGETR0(BuyDialog); + const ItemDB::ItemInfos &items = ItemDB::getItemInfos(); + FOR_EACH (ItemDB::ItemInfos::const_iterator, it, items) + { + const ItemInfo *const info = (*it).second; + if (info == nullptr) + continue; + const int id = info->getId(); + if (id <= 500) + continue; + + dialog->addItem(id, + ItemType::Unknown, + ItemColor_one, + 100, + 0); + } + dialog->sort(); + return true; +} + +impHandler(createItem) +{ + int id = 0; + int amount = 0; + + if (adminHandler == nullptr) + return false; + + if (parse2Int(event.args, id, amount)) + adminHandler->createItems(id, ItemColor_one, amount); + else + adminHandler->createItems(atoi(event.args.c_str()), ItemColor_one, 1); + return true; +} + +impHandler(uploadConfig) +{ + // TRANSLATORS: upload config chat message + uploadFile(_("Config uploaded to:"), + config.getFileName(), + "?xml", + event.tab); + return true; +} + +impHandler(uploadServerConfig) +{ + // TRANSLATORS: upload config chat message + uploadFile(_("Server config Uploaded to:"), + serverConfig.getFileName(), + "?xml", + event.tab); + return true; +} + +impHandler(uploadLog) +{ + // TRANSLATORS: upload log chat message + uploadFile(_("Log uploaded to:"), + settings.logFileName, + "", + event.tab); + return true; +} + +impHandler0(mercenaryFire) +{ + if (mercenaryHandler != nullptr) + mercenaryHandler->fire(); + return true; +} + +impHandler0(mercenaryToMaster) +{ + if (mercenaryHandler != nullptr) + mercenaryHandler->moveToMaster(); + return true; +} + +impHandler0(homunculusToMaster) +{ + if (homunculusHandler != nullptr) + homunculusHandler->moveToMaster(); + return true; +} + +impHandler0(homunculusFeed) +{ + if (homunculusHandler != nullptr) + homunculusHandler->feed(); + return true; +} + +impHandler(useItem) +{ + StringVect pars; + if (!splitParameters(pars, event.args, " ,", '\"')) + return false; + const int sz = CAST_S32(pars.size()); + if (sz < 1) + return false; + + const int itemId = atoi(pars[0].c_str()); + + if (itemId < SPELL_MIN_ID) + { + const Inventory *const inv = PlayerInfo::getInventory(); + if (inv != nullptr) + { + ItemColor color = ItemColor_one; + int16_t useType = 0; + StringVect pars2; + if (!splitParameters(pars2, pars[0], " ,", '\"')) + return false; + const int sz2 = CAST_S32(pars2.size()); + if (sz2 < 1) + return false; + if (sz2 >= 2) + color = fromInt(atoi(pars2[1].c_str()), ItemColor); + if (sz >= 2) + useType = CAST_S16(atoi(pars[1].c_str())); + const Item *const item = inv->findItem(itemId, + color); + PlayerInfo::useEquipItem(item, useType, Sfx_true); + } + } + else if (itemId < SKILL_MIN_ID && (spellManager != nullptr)) + { + spellManager->useItem(itemId); + } + else if (skillDialog != nullptr) + { + // +++ probably need get data parameter from args + skillDialog->useItem(itemId, + fromBool(config.getBoolValue("skillAutotarget"), AutoTarget), + 0, + std::string()); + } + return true; +} + +impHandler(useItemInv) +{ + int param1 = 0; + int param2 = 0; + const std::string args = event.args; + if (parse2Int(args, param1, param2)) + { + Item *const item = getItemByInvIndex(param1, + InventoryType::Inventory); + PlayerInfo::useEquipItem(item, CAST_S16(param2), Sfx_true); + } + else + { + Item *const item = getItemByInvIndex(atoi(event.args.c_str()), + InventoryType::Inventory); + PlayerInfo::useEquipItem(item, 0, Sfx_true); + } + return true; +} + +impHandler(invToStorage) +{ + Item *item = nullptr; + const int amount = getAmountFromEvent(event, item, + InventoryType::Inventory); + if (item == nullptr) + return true; + if (amount != 0) + { + if (inventoryHandler != nullptr) + { + inventoryHandler->moveItem2(InventoryType::Inventory, + item->getInvIndex(), + amount, + InventoryType::Storage); + } + } + else + { + ItemAmountWindow::showWindow(ItemAmountWindowUsage::StoreAdd, + inventoryWindow, + item, + 0, + 0); + } + return true; +} + +impHandler(tradeAdd) +{ + Item *item = nullptr; + const int amount = getAmountFromEvent(event, item, + InventoryType::Inventory); + if ((item == nullptr) || PlayerInfo::isItemProtected(item->getId())) + return true; + + if (amount != 0) + { + if (tradeWindow != nullptr) + tradeWindow->tradeItem(item, amount, true); + } + else + { + ItemAmountWindow::showWindow(ItemAmountWindowUsage::TradeAdd, + tradeWindow, + item, + 0, + 0); + } + return true; +} + +impHandler(storageToInv) +{ + Item *item = nullptr; + const int amount = getAmountFromEvent(event, item, InventoryType::Storage); + if (amount != 0) + { + if ((inventoryHandler != nullptr) && (item != nullptr)) + { + inventoryHandler->moveItem2(InventoryType::Storage, + item->getInvIndex(), + amount, + InventoryType::Inventory); + } + } + else + { + ItemAmountWindow::showWindow(ItemAmountWindowUsage::StoreRemove, + storageWindow, + item, + 0, + 0); + } + return true; +} + +impHandler(protectItem) +{ + const int id = atoi(event.args.c_str()); + if (id > 0) + PlayerInfo::protectItem(id); + return true; +} + +impHandler(unprotectItem) +{ + const int id = atoi(event.args.c_str()); + if (id > 0) + PlayerInfo::unprotectItem(id); + return true; +} + +impHandler(kick) +{ + if ((localPlayer == nullptr) || (actorManager == nullptr)) + return false; + + Being *target = nullptr; + std::string args = event.args; + if (!args.empty()) + { + if (args[0] != ':') + { + target = actorManager->findNearestByName(args, + ActorType::Unknown); + } + else + { + target = actorManager->findBeing(fromInt(atoi( + args.substr(1).c_str()), BeingId)); + } + } + if (target == nullptr) + target = localPlayer->getTarget(); + if ((target != nullptr) && (adminHandler != nullptr)) + adminHandler->kick(target->getId()); + return true; +} + +impHandler0(clearDrop) +{ + if (dropShortcut != nullptr) + dropShortcut->clear(true); + return true; +} + +impHandler0(testInfo) +{ + if (actorManager != nullptr) + { + logger->log("actors count: %d", CAST_S32( + actorManager->size())); + return true; + } + return false; +} + +impHandler(craftKey) +{ + const int slot = (event.action - InputAction::CRAFT_1); + if (slot >= 0 && slot < 9) + { + if (inventoryWindow != nullptr) + inventoryWindow->moveItemToCraft(slot); + return true; + } + return false; +} + +impHandler0(resetGameModifiers) +{ + GameModifiers::resetModifiers(); + return true; +} + +impHandler(barToChat) +{ + if (chatWindow != nullptr) + { + chatWindow->addInputText(event.args, + true); + return true; + } + return false; +} + +impHandler(seen) +{ + if (actorManager == nullptr) + return false; + + ChatTab *tab = event.tab; + if (tab == nullptr) + tab = localChatTab; + if (tab == nullptr) + return false; + + if (config.getBoolValue("enableIdCollecting") == false) + { + // TRANSLATORS: last seen disabled warning + tab->chatLog(_("Last seen disabled. " + "Enable in players / collect players ID and seen log."), + ChatMsgType::BY_SERVER, + IgnoreRecord_false, + TryRemoveColors_true); + return true; + } + + const std::string name = event.args; + if (name.empty()) + return false; + + std::string dir = settings.usersDir; + dir.append(stringToHexPath(name)).append("/seen.txt"); + if (Files::existsLocal(dir)) + { + StringVect lines; + Files::loadTextFileLocal(dir, lines); + if (lines.size() < 3) + { + // TRANSLATORS: last seen error + tab->chatLog(_("You have never seen this nick."), + ChatMsgType::BY_SERVER, + IgnoreRecord_false, + TryRemoveColors_true); + return true; + } + const std::string message = strprintf( + // TRANSLATORS: last seen message + _("Last seen for %s: %s"), + name.c_str(), + lines[2].c_str()); + tab->chatLog(message, + ChatMsgType::BY_SERVER, + IgnoreRecord_false, + TryRemoveColors_true); + } + else + { + // TRANSLATORS: last seen error + tab->chatLog(_("You have not seen this nick before."), + ChatMsgType::BY_SERVER, + IgnoreRecord_false, + TryRemoveColors_true); + } + + return true; +} + +impHandler(dumpMemoryUsage) +{ + if (event.tab != nullptr) + MemoryManager::printAllMemory(event.tab); + else + MemoryManager::printAllMemory(localChatTab); + return true; +} + +impHandler(setEmoteType) +{ + const std::string &args = event.args; + if (args == "player" || args.empty()) + { + settings.emoteType = EmoteType::Player; + } + else if (args == "pet") + { + settings.emoteType = EmoteType::Pet; + } + else if (args == "homun" || args == "homunculus") + { + settings.emoteType = EmoteType::Homunculus; + } + else if (args == "merc" || args == "mercenary") + { + settings.emoteType = EmoteType::Mercenary; + } + return true; +} + +} // namespace Actions diff --git a/src/progs/manaverse/actions/chat.cpp b/src/progs/manaverse/actions/chat.cpp new file mode 100644 index 000000000..27963a75f --- /dev/null +++ b/src/progs/manaverse/actions/chat.cpp @@ -0,0 +1,840 @@ +/* + * The ManaPlus Client + * Copyright (C) 2012-2020 The ManaPlus Developers + * Copyright (C) 2020-2023 The ManaVerse Developers + * + * This file is part of The ManaPlus Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * 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 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 <http://www.gnu.org/licenses/>. + */ + +#include "actions/chat.h" + +#include "configuration.h" + +#include "actions/actiondef.h" + +#include "being/localplayer.h" + +#include "gui/sdlinput.h" + +#include "gui/windows/chatwindow.h" + +#include "listeners/inputactionreplaylistener.h" + +#include "net/charserverhandler.h" +#include "net/chathandler.h" +#include "net/clanhandler.h" +#include "net/guildhandler.h" +#include "net/net.h" +#include "net/partyhandler.h" + +#ifdef TMWA_SUPPORT +#include "net/tmwa/guildmanager.h" +#endif // TMWA_SUPPORT + +#include "resources/iteminfo.h" + +#include "resources/db/itemdb.h" + +#include "utils/booleanoptions.h" +#include "utils/chatutils.h" +#include "utils/parameters.h" + +#include "utils/translation/podict.h" + +#include "debug.h" + +const int DEFAULT_CHAT_WINDOW_SCROLL = 7; + +namespace Actions +{ + +static void outString(ChatTab *const tab, + const std::string &str, + const std::string &def) +{ + if (tab == nullptr) + { + if (chatHandler != nullptr) + chatHandler->talk(def); + return; + } + + switch (tab->getType()) + { + case ChatTabType::CLAN: + { + if (clanHandler != nullptr) + clanHandler->chat(str); + break; + } + case ChatTabType::PARTY: + { + if (partyHandler != nullptr) + partyHandler->chat(str); + break; + } + case ChatTabType::GUILD: + { + if ((guildHandler == nullptr) || (localPlayer == nullptr)) + return; + const Guild *const guild = localPlayer->getGuild(); + if (guild != nullptr) + { +#ifdef TMWA_SUPPORT + if (guild->getServerGuild()) + { + if (Net::getNetworkType() == ServerType::TMWATHENA) + return; + guildHandler->chat(str); + } + else if (guildManager != nullptr) + { + guildManager->chat(str); + } +#else // TMWA_SUPPORT + + if (guild->getServerGuild()) + guildHandler->chat(str); +#endif // TMWA_SUPPORT + } + break; + } + case ChatTabType::CHANNEL: + case ChatTabType::GM: + case ChatTabType::TRADE: + tab->chatInput(str); + break; + default: + case ChatTabType::UNKNOWN: + case ChatTabType::INPUT: + case ChatTabType::WHISPER: + case ChatTabType::DEBUG: + case ChatTabType::BATTLE: + case ChatTabType::LANG: + if (chatHandler != nullptr) + chatHandler->talk(str); + break; + } +} + +impHandler0(toggleChat) +{ + return chatWindow != nullptr ? chatWindow->requestChatFocus() : false; +} + +impHandler0(prevChatTab) +{ + if (chatWindow != nullptr) + { + chatWindow->prevTab(); + return true; + } + return false; +} + +impHandler0(nextChatTab) +{ + if (chatWindow != nullptr) + { + chatWindow->nextTab(); + return true; + } + return false; +} + +impHandler0(closeChatTab) +{ + if (chatWindow != nullptr) + { + chatWindow->closeTab(); + return true; + } + return false; +} + +impHandler0(closeAllChatTabs) +{ + if (chatWindow != nullptr) + { + chatWindow->removeAllWhispers(); + chatWindow->saveState(); + return true; + } + return false; +} + +impHandler0(ignoreAllWhispers) +{ + if (chatWindow != nullptr) + { + chatWindow->ignoreAllWhispers(); + chatWindow->saveState(); + return true; + } + return false; +} + +impHandler0(scrollChatUp) +{ + if ((chatWindow != nullptr) && chatWindow->isWindowVisible()) + { + chatWindow->scroll(-DEFAULT_CHAT_WINDOW_SCROLL); + return true; + } + return false; +} + +impHandler0(scrollChatDown) +{ + if ((chatWindow != nullptr) && chatWindow->isWindowVisible()) + { + chatWindow->scroll(DEFAULT_CHAT_WINDOW_SCROLL); + return true; + } + return false; +} + +static bool splitWhisper(const std::string &args, + std::string &recvnick, + std::string &message) +{ + if (args.substr(0, 1) == "\"") + { + const size_t pos = args.find('"', 1); + if (pos != std::string::npos) + { + recvnick = args.substr(1, pos - 1); + if (pos + 2 < args.length()) + message = args.substr(pos + 2, args.length()); + } + } + else + { + const size_t pos = args.find(' '); + if (pos != std::string::npos) + { + recvnick = args.substr(0, pos); + if (pos + 1 < args.length()) + message = args.substr(pos + 1, args.length()); + } + else + { + recvnick = std::string(args); + message.clear(); + } + } + + trim(message); + + if (message.length() > 0) + { + std::string playerName = localPlayer->getName(); + std::string tempNick = recvnick; + + toLower(playerName); + toLower(tempNick); + + if (tempNick == playerName || args.empty()) + return false; + + return true; + } + return false; +} + +impHandler(msg) +{ + std::string recvnick; + std::string message; + + if (splitWhisper(event.args, recvnick, message)) + { + if (chatWindow == nullptr) + return false; + ChatTab *const tab = chatWindow->addChatTab(recvnick, false, true); + if (tab != nullptr) + { + chatWindow->saveState(); + tab->chatInput(message); + } + } + else + { + if (event.tab != nullptr) + { + event.tab->chatLog( + // TRANSLATORS: whisper send + _("Cannot send empty whisper or channel message!"), + ChatMsgType::BY_SERVER, + IgnoreRecord_false, + TryRemoveColors_true); + } + } + return true; +} + +impHandler(msgText) +{ + if (chatWindow == nullptr) + return false; + + if (config.getBoolValue("whispertab")) + { + chatWindow->localChatInput("/q " + event.args); + } + else + { + chatWindow->addInputText( + std::string("/w \"").append(event.args).append("\" "), + true); + } + return true; +} + +impHandler(msg2) +{ + std::string recvnick; + std::string message; + + if (chatHandler != nullptr && + splitWhisper(event.args, recvnick, message)) + { + chatHandler->privateMessage(recvnick, message); + } + return true; +} + +impHandler(query) +{ + const std::string &args = event.args; + if (chatWindow != nullptr) + { + if (chatWindow->addChatTab(args, true, true) != nullptr) + { + chatWindow->saveState(); + return true; + } + } + + if (event.tab != nullptr) + { + // TRANSLATORS: new whisper or channel query + event.tab->chatLog(strprintf(_("Cannot create a whisper tab " + "\"%s\"! It probably already exists."), + args.c_str()), + ChatMsgType::BY_SERVER, + IgnoreRecord_false, + TryRemoveColors_true); + } + return true; +} + +impHandler0(clearChatTab) +{ + if (chatWindow != nullptr) + { + chatWindow->clearTab(); + return true; + } + return false; +} + +impHandler(createParty) +{ + if (partyHandler == nullptr) + return false; + + if (event.args.empty()) + { + // TRANSLATORS: dialog header + inputActionReplayListener.openDialog(_("Create party"), + "", + InputAction::CREATE_PARTY); + } + else + { + partyHandler->create(event.args); + } + return true; +} + +impHandler(createGuild) +{ + if ((guildHandler == nullptr) || + Net::getNetworkType() == ServerType::TMWATHENA) + { + return false; + } + + if (event.args.empty()) + { + // TRANSLATORS: dialog header + inputActionReplayListener.openDialog(_("Create guild"), + "", + InputAction::CREATE_GUILD); + } + else + { + guildHandler->create(event.args); + } + return true; +} + +impHandler(party) +{ + if (!event.args.empty()) + { + if (partyHandler != nullptr) + partyHandler->invite(event.args); + } + else + { + if (event.tab != nullptr) + { + // TRANSLATORS: party invite message + event.tab->chatLog(_("Please specify a name."), + ChatMsgType::BY_SERVER, + IgnoreRecord_false, + TryRemoveColors_true); + } + } + return true; +} + +impHandler(guild) +{ + if ((guildHandler == nullptr) || (localPlayer == nullptr)) + return false; + + const std::string args = event.args; + if (!args.empty()) + { + const Guild *const guild = localPlayer->getGuild(); + if (guild != nullptr) + { +#ifdef TMWA_SUPPORT + if (guild->getServerGuild()) + guildHandler->invite(args); + else if (guildManager != nullptr) + GuildManager::invite(args); +#else // TMWA_SUPPORT + + guildHandler->invite(args); +#endif // TMWA_SUPPORT + } + } + else + { + if (event.tab != nullptr) + { + // TRANSLATORS: guild invite message + event.tab->chatLog(_("Please specify a name."), + ChatMsgType::BY_SERVER, + IgnoreRecord_false, + TryRemoveColors_true); + } + else if (localChatTab != nullptr) + { + // TRANSLATORS: guild invite message + localChatTab->chatLog(_("Please specify a name."), + ChatMsgType::BY_SERVER, + IgnoreRecord_false, + TryRemoveColors_true); + } + } + return true; +} + +impHandler(me) +{ + outString(event.tab, textToMe(event.args), event.args); + return true; +} + +impHandler(toggle) +{ + if (event.args.empty()) + { + if ((chatWindow != nullptr) && (event.tab != nullptr)) + { + event.tab->chatLog(chatWindow->getReturnTogglesChat() ? + // TRANSLATORS: message from toggle chat command + _("Return toggles chat.") : _("Message closes chat."), + ChatMsgType::BY_SERVER, + IgnoreRecord_false, + TryRemoveColors_true); + } + return true; + } + + switch (parseBoolean(event.args)) + { + case 1: + if (event.tab != nullptr) + { + // TRANSLATORS: message from toggle chat command + event.tab->chatLog(_("Return now toggles chat."), + ChatMsgType::BY_SERVER, + IgnoreRecord_false, + TryRemoveColors_true); + } + if (chatWindow != nullptr) + chatWindow->setReturnTogglesChat(true); + return true; + case 0: + if (event.tab != nullptr) + { + // TRANSLATORS: message from toggle chat command + event.tab->chatLog(_("Message now closes chat."), + ChatMsgType::BY_SERVER, + IgnoreRecord_false, + TryRemoveColors_true); + } + if (chatWindow != nullptr) + chatWindow->setReturnTogglesChat(false); + return true; + case -1: + if (event.tab != nullptr) + { + event.tab->chatLog(strprintf(BOOLEAN_OPTIONS, "toggle"), + ChatMsgType::BY_SERVER, + IgnoreRecord_false, + TryRemoveColors_true); + } + return true; + default: + return true; + } +} + +impHandler(kickParty) +{ + if (!event.args.empty()) + { + if (partyHandler != nullptr) + partyHandler->kick(event.args); + } + else + { + if (event.tab != nullptr) + { + // TRANSLATORS: party kick message + event.tab->chatLog(_("Please specify a name."), + ChatMsgType::BY_SERVER, + IgnoreRecord_false, + TryRemoveColors_true); + } + } + return true; +} + +impHandler(kickGuild) +{ + if (!event.args.empty()) + { + if (localPlayer != nullptr) + { + const Guild *const guild = localPlayer->getGuild(); + if (guild != nullptr) + { + if (guild->getServerGuild()) + { + if (guildHandler != nullptr) + guildHandler->kick(guild->getMember(event.args), ""); + } +#ifdef TMWA_SUPPORT + else if (guildManager != nullptr) + { + GuildManager::kick(event.args); + } +#endif // TMWA_SUPPORT + } + } + } + else + { + if (event.tab != nullptr) + { + // TRANSLATORS: party kick message + event.tab->chatLog(_("Please specify a name."), + ChatMsgType::BY_SERVER, + IgnoreRecord_false, + TryRemoveColors_true); + } + } + return true; +} + +impHandler(addText) +{ + if (chatWindow != nullptr) + chatWindow->addInputText(event.args, true); + return true; +} + +impHandler0(clearChat) +{ + if (chatWindow != nullptr) + chatWindow->clearTab(); + return true; +} + +impHandler0(chatGeneralTab) +{ + if (chatWindow != nullptr) + chatWindow->selectTabByType(ChatTabType::INPUT); + return true; +} + +impHandler0(chatDebugTab) +{ + if (chatWindow != nullptr) + chatWindow->selectTabByType(ChatTabType::DEBUG); + return true; +} + +impHandler0(chatBattleTab) +{ + if (chatWindow != nullptr) + chatWindow->selectTabByType(ChatTabType::BATTLE); + return true; +} + +impHandler0(chatTradeTab) +{ + if (chatWindow != nullptr) + chatWindow->selectTabByType(ChatTabType::TRADE); + return true; +} + +impHandler0(chatLangTab) +{ + if (chatWindow != nullptr) + chatWindow->selectTabByType(ChatTabType::LANG); + return true; +} + +impHandler0(chatGmTab) +{ + if (chatWindow != nullptr) + chatWindow->selectTabByType(ChatTabType::GM); + return true; +} + +impHandler0(chatPartyTab) +{ + if (chatWindow != nullptr) + chatWindow->selectTabByType(ChatTabType::PARTY); + return true; +} + +impHandler0(chatGuildTab) +{ + if (chatWindow != nullptr) + chatWindow->selectTabByType(ChatTabType::GUILD); + return true; +} + +impHandler(hat) +{ + if ((localPlayer == nullptr) || (charServerHandler == nullptr)) + return false; + + const int sprite = localPlayer->getSpriteID( + charServerHandler->hatSprite()); + std::string str; + if (sprite == 0) + { + // TRANSLATORS: equipped hat chat message + str = _("no hat equipped."); + } + else + { + const ItemInfo &info = ItemDB::get(sprite); + // TRANSLATORS: equipped hat chat message + str = strprintf(_("equipped hat %s."), + info.getName().c_str()); + } + outString(event.tab, str, str); + return true; +} + +impHandler(chatClipboard) +{ + int x = 0; + int y = 0; + + if ((chatWindow != nullptr) && parse2Int(event.args, x, y)) + { + chatWindow->copyToClipboard(x, y); + return true; + } + return false; +} + +impHandler(guildNotice) +{ + if (localPlayer == nullptr) + return false; + const std::string args = event.args; + if (args.empty()) + { + // TRANSLATORS: dialog header + inputActionReplayListener.openDialog(_("Guild notice"), + "", + InputAction::GUILD_NOTICE); + return true; + } + + std::string str2; + if (args.size() > 60) + str2 = args.substr(60); + const Guild *const guild = localPlayer->getGuild(); + if (guild != nullptr) + { + guildHandler->changeNotice(guild->getId(), + args.substr(0, 60), + str2); + } + return true; +} + +impHandler(translate) +{ + if (reverseDictionary == nullptr || + localPlayer == nullptr || + event.args.empty()) + { + return false; + } + + ChatTab *const tab = event.tab; + if (tab == nullptr) + return false; + + std::string srcStr = event.args; + std::string enStr; + toLower(srcStr); + if (localPlayer->getLanguageId() > 0) + { + if (reverseDictionary->haveStr(srcStr)) + enStr = reverseDictionary->getStr(srcStr); + else if (dictionary->haveStr(srcStr)) + enStr = srcStr; + } + else + { + if (dictionary->haveStr(srcStr)) + enStr = srcStr; + } + + if (enStr.empty()) + { + tab->chatLog( + // TRANSLATORS: translation error message + strprintf(_("No translation found for string: %s"), + srcStr.c_str()), + ChatMsgType::BY_SERVER, + IgnoreRecord_false, + TryRemoveColors_true); + return true; + } + + tab->chatInput(enStr); + return true; +} + +impHandler(sendGuiKey) +{ + if (guiInput == nullptr) + return false; + + const std::string args = event.args; + if (args.empty()) + return false; + StringVect pars; + if (!splitParameters(pars, args, " ,", '\"')) + return false; + const int sz = CAST_S32(pars.size()); + if (sz < 1) + return false; + + int keyValue = atoi(pars[0].c_str()); + if (keyValue == 0 && + pars[0].size() == 1) + { + keyValue = CAST_S32(pars[0][0]); + } + if (sz == 2) + { + const InputActionT actionId = InputManager::getActionByConfigField( + pars[1]); + guiInput->simulateKey(keyValue, actionId); + } + else + { + guiInput->simulateKey(keyValue, InputAction::NO_VALUE); + } + return true; +} + +impHandler(sendMouseKey) +{ + if (guiInput == nullptr) + return false; + const std::string args = event.args; + if (args.empty()) + return false; + StringVect pars; + if (!splitParameters(pars, args, " ,", '\"')) + return false; + const int sz = CAST_S32(pars.size()); + if (sz != 3) + return false; + + const int x = atoi(pars[0].c_str()); + const int y = atoi(pars[1].c_str()); + const int key1 = CAST_S32(MouseButton::LEFT); + const int key2 = CAST_S32(MouseButton::MIDDLE); + const int key = atoi(pars[2].c_str()); + if (key < key1 || key > key2) + return false; + guiInput->simulateMouseClick(x, + y, + static_cast<MouseButtonT>(key)); + return true; +} + +impHandler(sendChars) +{ + if (guiInput == nullptr) + return false; + + const std::string args = event.args; + if (args.empty()) + return false; + + const size_t sz = args.size(); + for (size_t f = 0; f < sz; f ++) + { + guiInput->simulateKey(CAST_S32(args[f]), + InputAction::NO_VALUE); + } + + return true; +} + +} // namespace Actions diff --git a/src/progs/manaverse/actions/commands.cpp b/src/progs/manaverse/actions/commands.cpp new file mode 100644 index 000000000..3f23fe5e3 --- /dev/null +++ b/src/progs/manaverse/actions/commands.cpp @@ -0,0 +1,2225 @@ +/* + * The ManaPlus Client + * Copyright (C) 2012-2020 The ManaPlus Developers + * Copyright (C) 2020-2023 The ManaVerse Developers + * + * This file is part of The ManaPlus Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * 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 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 <http://www.gnu.org/licenses/>. + */ + +#include "actions/commands.h" + +#include "actormanager.h" +#include "configuration.h" +#include "game.h" +#include "party.h" + +#include "actions/actiondef.h" + +#include "being/flooritem.h" +#include "being/localplayer.h" +#include "being/playerrelations.h" +#include "being/homunculusinfo.h" +#include "being/playerinfo.h" + +#include "const/resources/skill.h" + +#include "gui/viewport.h" + +#include "gui/popups/popupmenu.h" + +#include "gui/shortcut/emoteshortcut.h" +#include "gui/shortcut/itemshortcut.h" + +#include "gui/windows/mailwindow.h" + +#include "gui/windows/chatwindow.h" +#include "gui/windows/inventorywindow.h" +#include "gui/windows/npcdialog.h" +#include "gui/windows/outfitwindow.h" +#include "gui/windows/shortcutwindow.h" +#include "gui/windows/skilldialog.h" +#include "gui/windows/socialwindow.h" + +#include "gui/widgets/tabs/chat/whispertab.h" + +#include "input/inputactionoperators.h" + +#include "listeners/inputactionreplaylistener.h" + +#include "net/adminhandler.h" +#include "net/chathandler.h" +#include "net/guildhandler.h" +#include "net/familyhandler.h" +#include "net/homunculushandler.h" +#include "net/mail2handler.h" +#include "net/mailhandler.h" +#include "net/net.h" +#include "net/npchandler.h" +#include "net/partyhandler.h" +#include "net/serverfeatures.h" + +#include "resources/chatobject.h" + +#include "resources/db/itemdb.h" + +#include "resources/map/map.h" + +#include "resources/skill/skillinfo.h" + +#include "utils/booleanoptions.h" +#include "utils/chatutils.h" +#include "utils/copynpaste.h" +#include "utils/gmfunctions.h" +#include "utils/parameters.h" +#include "utils/process.h" + +#ifdef HAVE_MALLOC_TRIM +#include <malloc.h> +#endif + +#include "debug.h" + +namespace Actions +{ + +static std::string getNick(const InputEvent &event) +{ + std::string args = event.args; + if (args.empty()) + { + if (event.tab == nullptr || + event.tab->getType() != ChatTabType::WHISPER) + { + return std::string(); + } + + WhisperTab *const whisper = static_cast<WhisperTab *>(event.tab); + if (whisper->getNick().empty()) + { + // TRANSLATORS: change relation + event.tab->chatLog(_("Please specify a name."), + ChatMsgType::BY_SERVER, + IgnoreRecord_false, + TryRemoveColors_true); + return std::string(); + } + args = whisper->getNick(); + } + return args; +} + +static void reportRelation(const InputEvent &event, + const RelationT &rel, + const std::string &str1, + const std::string &str2) +{ + if (event.tab != nullptr) + { + if (playerRelations.getRelation(event.args) == rel) + { + // TRANSLATORS: unignore command + event.tab->chatLog(str1, + ChatMsgType::BY_SERVER, + IgnoreRecord_false, + TryRemoveColors_true); + } + else + { + // TRANSLATORS: unignore command + event.tab->chatLog(str2, + ChatMsgType::BY_SERVER, + IgnoreRecord_false, + TryRemoveColors_true); + } + } +} + +static void changeRelation(const InputEvent &event, + const RelationT relation, + const std::string &relationText) +{ + std::string args = getNick(event); + if (args.empty()) + return; + + if (playerRelations.getRelation(args) == relation) + { + if (event.tab != nullptr) + { + // TRANSLATORS: change relation + event.tab->chatLog(strprintf(_("Player already %s!"), + relationText.c_str()), + ChatMsgType::BY_SERVER, + IgnoreRecord_false, + TryRemoveColors_true); + return; + } + } + else + { + playerRelations.setRelation(args, relation); + } + + reportRelation(event, + relation, + // TRANSLATORS: change relation + strprintf(_("Player successfully %s!"), relationText.c_str()), + // TRANSLATORS: change relation + strprintf(_("Player could not be %s!"), relationText.c_str())); +} + +impHandler(chatAnnounce) +{ + if (adminHandler != nullptr) + { + adminHandler->announce(event.args); + return true; + } + return false; +} + +impHandler(chatIgnore) +{ + changeRelation(event, Relation::IGNORED, "ignored"); + return true; +} + +impHandler(chatUnignore) +{ + std::string args = getNick(event); + if (args.empty()) + return false; + + const RelationT rel = playerRelations.getRelation(args); + if (rel != Relation::NEUTRAL && rel != Relation::FRIEND) + { + playerRelations.setRelation(args, Relation::NEUTRAL); + } + else + { + if (event.tab != nullptr) + { + // TRANSLATORS: unignore command + event.tab->chatLog(_("Player wasn't ignored!"), + ChatMsgType::BY_SERVER, + IgnoreRecord_false, + TryRemoveColors_true); + } + return true; + } + + reportRelation(event, + Relation::NEUTRAL, + // TRANSLATORS: unignore command + _("Player no longer ignored!"), + // TRANSLATORS: unignore command + _("Player could not be unignored!")); + return true; +} + +impHandler(chatErase) +{ + std::string args = getNick(event); + if (args.empty()) + return false; + + if (playerRelations.getRelation(args) == Relation::ERASED) + { + if (event.tab != nullptr) + { + // TRANSLATORS: erase command + event.tab->chatLog(_("Player already erased!"), + ChatMsgType::BY_SERVER, + IgnoreRecord_false, + TryRemoveColors_true); + } + return true; + } + playerRelations.setRelation(args, Relation::ERASED); + + reportRelation(event, + Relation::ERASED, + // TRANSLATORS: erase command + _("Player no longer erased!"), + // TRANSLATORS: erase command + _("Player could not be erased!")); + return true; +} + +impHandler(chatFriend) +{ + // TRANSLATORS: adding friend command + changeRelation(event, Relation::FRIEND, _("friend")); + return true; +} + +impHandler(chatDisregard) +{ + // TRANSLATORS: disregard command + changeRelation(event, Relation::DISREGARDED, _("disregarded")); + return true; +} + +impHandler(chatNeutral) +{ + // TRANSLATORS: neutral command + changeRelation(event, Relation::NEUTRAL, _("neutral")); + return true; +} + +impHandler(chatBlackList) +{ + // TRANSLATORS: blacklist command + changeRelation(event, Relation::BLACKLISTED, _("blacklisted")); + return true; +} + +impHandler(chatEnemy) +{ + // TRANSLATORS: enemy command + changeRelation(event, Relation::ENEMY2, _("enemy")); + return true; +} + +impHandler(chatNuke) +{ + if (actorManager == nullptr) + return false; + + const std::string nick = getNick(event); + Being *const being = actorManager->findBeingByName( + nick, ActorType::Player); + if (being == nullptr) + return true; + + actorManager->addBlock(being->getId()); + actorManager->destroy(being); + return true; +} + +impHandler(chatAdd) +{ + if (chatWindow == nullptr) + return false; + + if (event.args.empty()) + return true; + + STD_VECTOR<int> str; + splitToIntVector(str, event.args, ','); + if (str.empty()) + return true; + + int id = str[0]; + if (id == 0) + return true; + + if (ItemDB::exists(id)) + { + const std::string names = ItemDB::getNamesStr(str); + if (!names.empty()) + chatWindow->addItemText(names); + return true; + } + + const FloorItem *const floorItem = actorManager->findItem( + fromInt(id, BeingId)); + + if (floorItem != nullptr) + { + str[0] = floorItem->getItemId(); + const std::string names = ItemDB::getNamesStr(str); + chatWindow->addItemText(names); + } + return true; +} + +impHandler0(present) +{ + if (chatWindow != nullptr) + { + chatWindow->doPresent(); + return true; + } + return false; +} + +impHandler0(printAll) +{ + if (actorManager != nullptr) + { + actorManager->printAllToChat(); + return true; + } + return false; +} + +impHandler(move) +{ + int x = 0; + int y = 0; + + if ((localPlayer != nullptr) && parse2Int(event.args, x, y)) + { + localPlayer->setDestination(x, y); + return true; + } + return false; +} + +impHandler(setTarget) +{ + if ((actorManager == nullptr) || (localPlayer == nullptr)) + return false; + + Being *const target = actorManager->findNearestByName(event.args, + ActorType::Unknown); + if (target != nullptr) + localPlayer->setTarget(target); + return true; +} + +impHandler(commandOutfit) +{ + if (outfitWindow != nullptr) + { + if (!event.args.empty()) + { + const std::string op = event.args.substr(0, 1); + if (op == "n") + { + outfitWindow->wearNextOutfit(true); + } + else if (op == "p") + { + outfitWindow->wearPreviousOutfit(true); + } + else + { + outfitWindow->wearOutfit(atoi(event.args.c_str()) - 1, + false, true); + } + } + else + { + outfitWindow->wearOutfit(atoi(event.args.c_str()) - 1, + false, true); + } + return true; + } + return false; +} + +impHandler(commandEmote) +{ + LocalPlayer::emote(CAST_U8(atoi(event.args.c_str()))); + return true; +} + +impHandler(awayMessage) +{ + if (localPlayer != nullptr) + { + localPlayer->setAway(event.args); + return true; + } + return false; +} + +impHandler(pseudoAway) +{ + if (localPlayer != nullptr) + { + LocalPlayer::setPseudoAway(event.args); + localPlayer->updateStatus(); + return true; + } + return false; +} + +impHandler(follow) +{ + if (localPlayer == nullptr) + return false; + + if (!features.getBoolValue("allowFollow")) + return false; + + if (!event.args.empty()) + { + localPlayer->setFollow(event.args); + } + else if (event.tab != nullptr && + event.tab->getType() == ChatTabType::WHISPER) + { + localPlayer->setFollow(static_cast<WhisperTab*>(event.tab)->getNick()); + } + else + { + const Being *const being = localPlayer->getTarget(); + if (being != nullptr) + localPlayer->setFollow(being->getName()); + } + return true; +} + +impHandler(navigate) +{ + if ((localPlayer == nullptr) || + !localPlayer->canMove()) + { + return false; + } + + int x = 0; + int y = 0; + + if (parse2Int(event.args, x, y)) + localPlayer->navigateTo(x, y); + else + localPlayer->navigateClean(); + return true; +} + +impHandler(navigateTo) +{ + if ((localPlayer == nullptr) || + !localPlayer->canMove()) + { + return false; + } + + const std::string args = event.args; + if (args.empty()) + return true; + + Being *const being = actorManager->findBeingByName(args, + ActorType::Unknown); + if (being != nullptr) + { + localPlayer->navigateTo(being->getTileX(), being->getTileY()); + } + else if (localPlayer->isInParty()) + { + const Party *const party = localPlayer->getParty(); + if (party != nullptr) + { + const PartyMember *const m = party->getMember(args); + const PartyMember *const o = party->getMember( + localPlayer->getName()); + if (m != nullptr && + o != nullptr && + m->getMap() == o->getMap()) + { + localPlayer->navigateTo(m->getX(), m->getY()); + } + } + } + return true; +} + +impHandler(moveCamera) +{ + int x = 0; + int y = 0; + + if (viewport == nullptr) + return false; + + if (parse2Int(event.args, x, y)) + viewport->moveCameraToPosition(x * mapTileSize, y * mapTileSize); + return true; +} + +impHandler0(restoreCamera) +{ + if (viewport == nullptr) + return false; + + viewport->returnCamera(); + return true; +} + +impHandler(imitation) +{ + if (localPlayer == nullptr) + return false; + + if (!event.args.empty()) + { + localPlayer->setImitate(event.args); + } + else if (event.tab != nullptr && + event.tab->getType() == ChatTabType::WHISPER) + { + localPlayer->setImitate(static_cast<WhisperTab*>( + event.tab)->getNick()); + } + else + { + localPlayer->setImitate(""); + } + return true; +} + +impHandler(sendMail) +{ +#ifdef TMWA_SUPPORT + const ServerTypeT type = Net::getNetworkType(); + if (type == ServerType::EATHENA || type == ServerType::EVOL2) +#endif // TMWA_SUPPORT + { + std::string name; + std::string text; + + if (parse2Str(event.args, name, text)) + { + if (settings.enableNewMailSystem) + { + mail2Handler->queueCheckName(MailQueueType::SendMail, + name, + // TRANSLATORS: quick mail message caption + _("Quick message"), + text, + 0); + } + else + { + // TRANSLATORS: quick mail message caption + mailHandler->send(name, _("Quick message"), text); + } + } + } +#ifdef TMWA_SUPPORT + else if (serverConfig.getBoolValue("enableManaMarketBot")) + { + chatHandler->privateMessage("ManaMarket", "!mail " + event.args); + return true; + } +#endif // TMWA_SUPPORT + + return false; +} + +impHandler(info) +{ + if (event.tab == nullptr || + localPlayer == nullptr || + Net::getNetworkType() == ServerType::TMWATHENA) + { + return false; + } + + if (guildHandler != nullptr && + event.tab->getType() == ChatTabType::GUILD) + { + const Guild *const guild = localPlayer->getGuild(); + if (guild != nullptr) + guildHandler->info(); + } + return true; +} + +impHandler(wait) +{ + if (localPlayer != nullptr) + { + localPlayer->waitFor(event.args); + return true; + } + return false; +} + +impHandler(addPriorityAttack) +{ + if ((actorManager == nullptr) || + actorManager->isInPriorityAttackList(event.args)) + { + return false; + } + + actorManager->removeAttackMob(event.args); + actorManager->addPriorityAttackMob(event.args); + + if (socialWindow != nullptr) + socialWindow->updateAttackFilter(); + return true; +} + +impHandler(addAttack) +{ + if (actorManager == nullptr) + return false; + + actorManager->removeAttackMob(event.args); + actorManager->addAttackMob(event.args); + + if (socialWindow != nullptr) + socialWindow->updateAttackFilter(); + return true; +} + +impHandler(removeAttack) +{ + if (actorManager == nullptr) + return false; + + if (event.args.empty()) + { + if (actorManager->isInAttackList(event.args)) + { + actorManager->removeAttackMob(event.args); + actorManager->addIgnoreAttackMob(event.args); + } + else + { + actorManager->removeAttackMob(event.args); + actorManager->addAttackMob(event.args); + } + } + else + { + actorManager->removeAttackMob(event.args); + } + + + if (socialWindow != nullptr) + socialWindow->updateAttackFilter(); + return true; +} + +impHandler(addIgnoreAttack) +{ + if (actorManager == nullptr) + return false; + + actorManager->removeAttackMob(event.args); + actorManager->addIgnoreAttackMob(event.args); + + if (socialWindow != nullptr) + socialWindow->updateAttackFilter(); + return true; +} + +impHandler(setDrop) +{ + GameModifiers::setQuickDropCounter(atoi(event.args.c_str())); + return true; +} + +impHandler(url) +{ + if (event.tab != nullptr) + { + std::string url1 = event.args; + if (!strStartWith(url1, "http") && !strStartWith(url1, "ftp") && + !strStartWith(url1, "?")) + url1 = "http://" + url1; + std::string str(strprintf("[@@%s |%s@@]", + url1.c_str(), event.args.c_str())); + outStringNormal(event.tab, str, str); + return true; + } + return false; +} + +impHandler(openUrl) +{ + std::string url = event.args; + if (!strStartWith(url, "http") && !strStartWith(url, "ftp")) + url = "http://" + url; + openBrowser(url); + return true; +} + +impHandler(execute) +{ + const size_t idx = event.args.find(' '); + std::string name; + std::string params; + if (idx == std::string::npos) + { + name = event.args; + } + else + { + name = event.args.substr(0, idx); + params = event.args.substr(idx + 1); + } + execFile(name, name, params, ""); + return true; +} + +impHandler(enableHighlight) +{ + if (event.tab != nullptr) + { + event.tab->setAllowHighlight(true); + if (chatWindow != nullptr) + { + chatWindow->saveState(); + return true; + } + } + return false; +} + +impHandler(disableHighlight) +{ + if (event.tab != nullptr) + { + event.tab->setAllowHighlight(false); + if (chatWindow != nullptr) + { + chatWindow->saveState(); + return true; + } + } + return false; +} + +impHandler(dontRemoveName) +{ + if (event.tab != nullptr) + { + event.tab->setRemoveNames(false); + if (chatWindow != nullptr) + { + chatWindow->saveState(); + return true; + } + } + return false; +} + +impHandler(removeName) +{ + if (event.tab != nullptr) + { + event.tab->setRemoveNames(true); + if (chatWindow != nullptr) + { + chatWindow->saveState(); + return true; + } + } + return false; +} + +impHandler(disableAway) +{ + if (event.tab != nullptr) + { + event.tab->setNoAway(true); + if (chatWindow != nullptr) + { + chatWindow->saveState(); + return true; + } + } + return false; +} + +impHandler(enableAway) +{ + if (event.tab != nullptr) + { + event.tab->setNoAway(false); + if (chatWindow != nullptr) + { + chatWindow->saveState(); + return true; + } + } + return false; +} + +impHandler(testParticle) +{ + if (localPlayer != nullptr) + { + localPlayer->setTestParticle(event.args, true); + return true; + } + return false; +} + +impHandler(talkRaw) +{ + if (chatHandler != nullptr) + { + chatHandler->talkRaw(event.args); + return true; + } + return false; +} + +impHandler(gm) +{ + if (chatHandler != nullptr) + { + Gm::runCommand("wgm", event.args); + return true; + } + return false; +} + +impHandler(hack) +{ + if (chatHandler != nullptr) + { + chatHandler->sendRaw(event.args); + return true; + } + return false; +} + +impHandler(debugSpawn) +{ + if (localPlayer == nullptr) + return false; + int cnt = atoi(event.args.c_str()); + if (cnt < 1) + cnt = 1; + const int half = cnt / 2; + const Map *const map = localPlayer->getMap(); + int x1 = -half; + if (x1 < 0) + x1 = 0; + int y1 = x1; + int x2 = cnt - half; + if (x2 > map->getWidth()) + x2 = map->getWidth(); + int y2 = x2; + + for (int x = x1; x < x2; x ++) + { + for (int y = y1; y < y2; y ++) + ActorManager::cloneBeing(localPlayer, x, y, cnt); + } + return true; +} + +impHandler(serverIgnoreWhisper) +{ + std::string args = getNick(event); + if (args.empty()) + return false; + + if (chatHandler != nullptr) + { + chatHandler->ignore(args); + return true; + } + return false; +} + +impHandler(serverUnIgnoreWhisper) +{ + std::string args = getNick(event); + if (args.empty()) + return false; + + if (chatHandler != nullptr) + { + chatHandler->unIgnore(args); + return true; + } + return false; +} + +impHandler(setHomunculusName) +{ + const std::string args = event.args; + if (args.empty()) + { + const HomunculusInfo *const info = PlayerInfo::getHomunculus(); + if (info != nullptr) + { + // TRANSLATORS: dialog header + inputActionReplayListener.openDialog(_("Rename your homun"), + info->name, + InputAction::HOMUNCULUS_SET_NAME); + } + return false; + } + + if (homunculusHandler != nullptr) + { + homunculusHandler->setName(args); + return true; + } + return false; +} + +impHandler0(fireHomunculus) +{ + if (homunculusHandler != nullptr) + { + homunculusHandler->fire(); + return true; + } + return false; +} + +impHandler0(leaveParty) +{ + if (partyHandler != nullptr) + { + partyHandler->leave(); + return true; + } + return false; +} + +impHandler0(leaveGuild) +{ + if ((guildHandler != nullptr) && (localPlayer != nullptr)) + { + const Guild *const guild = localPlayer->getGuild(); + if (guild != nullptr) + guildHandler->leave(guild->getId()); + return true; + } + return false; +} + +impHandler(warp) +{ + int x = 0; + int y = 0; + + if ((adminHandler != nullptr) && + (Game::instance() != nullptr) && + parse2Int(event.args, x, y)) + { + adminHandler->warp(Game::instance()->getCurrentMapName(), + x, y); + return true; + } + return false; +} + +impHandler(homunTalk) +{ + if ((serverFeatures == nullptr) || !serverFeatures->haveTalkPet()) + return false; + + std::string args = event.args; + if (findCutFirst(args, "/me ")) + args = textToMe(args); + if (homunculusHandler != nullptr) + { + homunculusHandler->talk(args); + return true; + } + return false; +} + +impHandler(homunEmote) +{ + if ((serverFeatures == nullptr) || !serverFeatures->haveTalkPet()) + return false; + + if ((homunculusHandler != nullptr) && + event.action >= InputAction::HOMUN_EMOTE_1 && + event.action <= InputAction::HOMUN_EMOTE_48) + { + if (emoteShortcut != nullptr) + { + const int emotion = event.action - InputAction::HOMUN_EMOTE_1; + homunculusHandler->emote(emoteShortcut->getEmote(emotion)); + } + if (Game::instance() != nullptr) + Game::instance()->setValidSpeed(); + return true; + } + + return false; +} + +impHandler(commandHomunEmote) +{ + if ((serverFeatures == nullptr) || !serverFeatures->haveTalkPet()) + return false; + + if (homunculusHandler != nullptr) + { + homunculusHandler->emote(CAST_U8( + atoi(event.args.c_str()))); + return true; + } + return false; +} + +impHandler(createPublicChatRoom) +{ + if ((chatHandler == nullptr) || event.args.empty()) + return false; + chatHandler->createChatRoom(event.args, "", 100, true); + return true; +} + +impHandler(joinChatRoom) +{ + if (chatHandler == nullptr) + return false; + const std::string args = event.args; + if (args.empty()) + return false; + ChatObject *const chat = ChatObject::findByName(args); + if (chat == nullptr) + return false; + chatHandler->joinChat(chat, ""); + return true; +} + +impHandler0(leaveChatRoom) +{ + if (chatHandler != nullptr) + { + chatHandler->leaveChatRoom(); + return true; + } + return false; +} + +impHandler(confSet) +{ + std::string name; + std::string val; + + if (parse2Str(event.args, name, val)) + { + config.setValue(name, val); + return true; + } + return false; +} + +impHandler(serverConfSet) +{ + std::string name; + std::string val; + + if (parse2Str(event.args, name, val)) + { + serverConfig.setValue(name, val); + return true; + } + return false; +} + +impHandler(confGet) +{ + const std::string args = event.args; + if (args.empty()) + return false; + + // TRANSLATORS: result from command /confget + const std::string str = strprintf(_("Config value: %s"), + config.getStringValue(args).c_str()); + outStringNormal(event.tab, str, str); + return true; +} + +impHandler(serverConfGet) +{ + const std::string args = event.args; + if (args.empty()) + return false; + + // TRANSLATORS: result from command /serverconfget + const std::string str = strprintf(_("Server config value: %s"), + serverConfig.getStringValue(args).c_str()); + outStringNormal(event.tab, str, str); + return true; +} + +impHandler(slide) +{ + int x = 0; + int y = 0; + + if ((adminHandler != nullptr) && parse2Int(event.args, x, y)) + { + adminHandler->slide(x, y); + return true; + } + return false; +} + +impHandler(selectSkillLevel) +{ + int skill = 0; + int level = 0; + + if ((skillDialog != nullptr) && parse2Int(event.args, skill, level)) + { + skillDialog->selectSkillLevel(skill, level); + return true; + } + return false; +} + +impHandler(skill) +{ + StringVect vect; + splitToStringVector(vect, event.args, ' '); + const int sz = CAST_S32(vect.size()); + if (sz < 1) + return true; + const int skillId = atoi(vect[0].c_str()); + int level = 0; + std::string text; + if (sz > 1) + { + level = atoi(vect[1].c_str()); + if (sz > 2) + text = vect[2]; + } + // +++ add here also cast type and offsets + if (text.empty()) + { + SkillDialog::useSkill(skillId, + AutoTarget_true, + level, + false, + "", + CastType::Default, + 0, + 0); + } + else + { + SkillDialog::useSkill(skillId, + AutoTarget_true, + level, + true, + text, + CastType::Default, + 0, + 0); + } + return true; +} + +impHandler(craft) +{ + const std::string args = event.args; + if (args.empty() || (inventoryWindow == nullptr)) + return false; + + inventoryWindow->moveItemToCraft(atoi(args.c_str())); + return true; +} + +impHandler(npcClipboard) +{ + if (npcHandler != nullptr) + { + int x = 0; + int y = 0; + + NpcDialog *const dialog = npcHandler->getCurrentNpcDialog(); + + if ((dialog != nullptr) && parse2Int(event.args, x, y)) + { + dialog->copyToClipboard(x, y); + return true; + } + } + return false; +} + +impHandler(clipboardCopy) +{ + const std::string args = event.args; + if (args.empty()) + return false; + sendBuffer(args); + return true; +} + +impHandler(addPickup) +{ + if (actorManager != nullptr) + { + actorManager->removePickupItem(event.args); + actorManager->addPickupItem(event.args); + if (socialWindow != nullptr) + socialWindow->updatePickupFilter(); + return true; + } + return false; +} + +impHandler(removePickup) +{ + if (actorManager != nullptr) + { + if (event.args.empty()) + { // default pickup manipulation + if (actorManager->checkDefaultPickup()) + { + actorManager->removePickupItem(event.args); + actorManager->addIgnorePickupItem(event.args); + } + else + { + actorManager->removePickupItem(event.args); + actorManager->addPickupItem(event.args); + } + } + else + { // any other pickups + actorManager->removePickupItem(event.args); + } + if (socialWindow != nullptr) + socialWindow->updatePickupFilter(); + return true; + } + return false; +} + +impHandler(ignorePickup) +{ + if (actorManager != nullptr) + { + actorManager->removePickupItem(event.args); + actorManager->addIgnorePickupItem(event.args); + if (socialWindow != nullptr) + socialWindow->updatePickupFilter(); + return true; + } + return false; +} + +impHandler(monsterInfo) +{ + const std::string args = event.args; + if (args.empty()) + return false; + adminHandler->monsterInfo(args); + return true; +} + +impHandler(itemInfo) +{ + const std::string args = event.args; + if (args.empty()) + return false; + adminHandler->itemInfo(args); + return true; +} + +impHandler(whoDrops) +{ + const std::string args = event.args; + if (args.empty()) + return false; + adminHandler->whoDrops(args); + return true; +} + +impHandler(mobSearch) +{ + const std::string args = event.args; + if (args.empty()) + return false; + adminHandler->mobSearch(args); + return true; +} + +impHandler(mobSpawnSearch) +{ + const std::string args = event.args; + if (args.empty()) + return false; + adminHandler->mobSpawnSearch(args); + return true; +} + +impHandler(playerGmCommands) +{ + adminHandler->playerGmCommands(event.args); + return true; +} + +impHandler(playerCharGmCommands) +{ + adminHandler->playerCharGmCommands(event.args); + return true; +} + +impHandler(commandShowLevel) +{ + adminHandler->showLevel(event.args); + return true; +} + +impHandler(commandShowStats) +{ + adminHandler->showStats(event.args); + return true; +} + +impHandler(commandShowStorage) +{ + adminHandler->showStorageList(event.args); + return true; +} + +impHandler(commandShowCart) +{ + adminHandler->showCartList(event.args); + return true; +} + +impHandler(commandShowInventory) +{ + adminHandler->showInventoryList(event.args); + return true; +} + +impHandler(locatePlayer) +{ + const std::string args = event.args; + if (args.empty()) + return false; + adminHandler->locatePlayer(args); + return true; +} + +impHandler(commandShowAccountInfo) +{ + const std::string args = event.args; + if (args.empty()) + return false; + adminHandler->showAccountInfo(args); + return true; +} + +impHandler(commandSpawn) +{ + const std::string args = event.args; + if (args.empty()) + return false; + adminHandler->spawn(args); + return true; +} + +impHandler(commandSpawnSlave) +{ + const std::string args = event.args; + if (args.empty()) + return false; + adminHandler->spawnSlave(args); + return true; +} + +impHandler(commandSpawnClone) +{ + const std::string args = event.args; + if (args.empty()) + return false; + adminHandler->spawnClone(args); + return true; +} + +impHandler(commandSpawnSlaveClone) +{ + const std::string args = event.args; + if (args.empty()) + return false; + adminHandler->spawnSlaveClone(args); + return true; +} + +impHandler(commandSpawnEvilClone) +{ + const std::string args = event.args; + if (args.empty()) + return false; + adminHandler->spawnEvilClone(args); + return true; +} + +impHandler(commandSavePosition) +{ + adminHandler->savePosition(event.args); + return true; +} + +impHandler(commandLoadPosition) +{ + adminHandler->loadPosition(event.args); + return true; +} + +impHandler(commandRandomWarp) +{ + adminHandler->randomWarp(event.args); + return true; +} + +impHandler(commandGotoNpc) +{ + const std::string args = event.args; + if (args.empty()) + return false; + adminHandler->gotoNpc(args); + return true; +} + +impHandler(commandGotoPc) +{ + const std::string args = event.args; + if (args.empty()) + return false; + adminHandler->gotoName(args); + return true; +} + +impHandler(commandRecallPc) +{ + const std::string args = event.args; + if (args.empty()) + return false; + adminHandler->recallName(args); + return true; +} + +impHandler(commandIpCheck) +{ + const std::string args = event.args; + if (args.empty()) + return false; + adminHandler->ipcheckName(args); + return true; +} + +impHandler(commandKiller) +{ + adminHandler->killer(event.args); + return true; +} + +impHandler(commandKillable) +{ + adminHandler->killable(event.args); + return true; +} + +impHandler(commandHeal) +{ + adminHandler->heal(event.args); + return true; +} + +impHandler(commandAlive) +{ + adminHandler->alive(event.args); + return true; +} + +impHandler(commandDisguise) +{ + const std::string args = event.args; + if (args.empty()) + return false; + adminHandler->disguise(args); + return true; +} + +impHandler(commandImmortal) +{ + adminHandler->immortal(event.args); + return true; +} + +impHandler(commandHide) +{ + adminHandler->hide(event.args); + return true; +} + +impHandler(commandNuke) +{ + const std::string args = event.args; + if (args.empty()) + return false; + adminHandler->nuke(args); + return true; +} + +impHandler(commandKill) +{ + adminHandler->kill(event.args); + return true; +} + +impHandler(commandJail) +{ + const std::string args = event.args; + if (args.empty()) + return false; + adminHandler->jail(args); + return true; +} + +impHandler(commandUnjail) +{ + const std::string args = event.args; + if (args.empty()) + return false; + adminHandler->unjail(args); + return true; +} + +impHandler(commandNpcMove) +{ + const std::string args = event.args; + if (args.empty()) + return false; + StringVect pars; + if (!splitParameters(pars, args, " ,", '\"')) + return false; + + if (pars.size() != 3) + return false; + + adminHandler->npcMove(pars[0], + atoi(pars[1].c_str()), + atoi(pars[2].c_str())); + return true; +} + +impHandler(commandNpcHide) +{ + const std::string args = event.args; + if (args.empty()) + return false; + adminHandler->hideNpc(args); + return true; +} + +impHandler(commandNpcShow) +{ + const std::string args = event.args; + if (args.empty()) + return false; + adminHandler->showNpc(args); + return true; +} + +impHandler(commandChangePartyLeader) +{ + const std::string args = event.args; + if (args.empty()) + return false; + adminHandler->changePartyLeader(args); + return true; +} + +impHandler(commandPartyRecall) +{ + const std::string args = event.args; + if (args.empty()) + return false; + adminHandler->partyRecall(args); + return true; +} + +impHandler(commandBreakGuild) +{ + adminHandler->breakGuild(event.args); + return true; +} + +impHandler(commandGuildRecall) +{ + const std::string args = event.args; + if (args.empty()) + return false; + adminHandler->guildRecall(args); + return true; +} + +impHandler(mailTo) +{ + if (mailWindow == nullptr) + return false; + const std::string args = event.args; + if (settings.enableNewMailSystem) + { + mail2Handler->queueCheckName(MailQueueType::EditMail, + args, + std::string(), + std::string(), + 0); + } + else + { + MailWindow::createMail(args); + } + return true; +} + +impHandler(adoptChild) +{ + const std::string nick = getNick(event); + Being *const being = actorManager->findBeingByName( + nick, ActorType::Player); + if (being == nullptr) + return true; + familyHandler->askForChild(being); + return true; +} + +impHandler(showSkillLevels) +{ + const std::string args = event.args; + if (args.empty()) + return false; + const SkillInfo *restrict const skill = skillDialog->getSkill( + atoi(args.c_str())); + if (skill == nullptr) + return false; + popupMenu->showSkillLevelPopup(skill); + return true; +} + +impHandler(showSkillType) +{ + const std::string args = event.args; + if (args.empty()) + return false; + const SkillInfo *restrict const skill = skillDialog->getSkill( + atoi(args.c_str())); + if (skill == nullptr) + return false; + popupMenu->showSkillTypePopup(skill); + return true; +} + +impHandler(selectSkillType) +{ + int skill = 0; + int type = 0; + + if ((skillDialog != nullptr) && parse2Int(event.args, skill, type)) + { + skillDialog->selectSkillCastType(skill, + static_cast<CastTypeT>(type)); + return true; + } + return false; +} + +impHandler(showSkillOffsetX) +{ + const std::string args = event.args; + if (args.empty()) + return false; + const SkillInfo *restrict const skill = skillDialog->getSkill( + atoi(args.c_str())); + if (skill == nullptr) + return false; + popupMenu->showSkillOffsetPopup(skill, true); + return true; +} + +impHandler(showSkillOffsetY) +{ + const std::string args = event.args; + if (args.empty()) + return false; + const SkillInfo *restrict const skill = skillDialog->getSkill( + atoi(args.c_str())); + if (skill == nullptr) + return false; + popupMenu->showSkillOffsetPopup(skill, false); + return true; +} + +impHandler(setSkillOffsetX) +{ + int skill = 0; + int offset = 0; + + if ((skillDialog != nullptr) && parse2Int(event.args, skill, offset)) + { + skillDialog->setSkillOffsetX(skill, offset); + return true; + } + return false; +} + +impHandler(setSkillOffsetY) +{ + int skill = 0; + int offset = 0; + + if ((skillDialog != nullptr) && parse2Int(event.args, skill, offset)) + { + skillDialog->setSkillOffsetY(skill, offset); + return true; + } + return false; +} + +impHandler(partyItemShare) +{ + if (localPlayer == nullptr) + return false; + + if (localPlayer->isInParty() == false) + return true; + + ChatTab *tab = event.tab; + if (tab == nullptr) + tab = localChatTab; + if (tab == nullptr) + return true; + + const std::string args = event.args; + if (args.empty()) + { + switch (partyHandler->getShareItems()) + { + case PartyShare::YES: + // TRANSLATORS: chat message + tab->chatLog(_("Item sharing enabled."), + ChatMsgType::BY_SERVER, + IgnoreRecord_false, + TryRemoveColors_true); + return true; + case PartyShare::NO: + // TRANSLATORS: chat message + tab->chatLog(_("Item sharing disabled."), + ChatMsgType::BY_SERVER, + IgnoreRecord_false, + TryRemoveColors_true); + return true; + case PartyShare::NOT_POSSIBLE: + // TRANSLATORS: chat message + tab->chatLog(_("Item sharing not possible."), + ChatMsgType::BY_SERVER, + IgnoreRecord_false, + TryRemoveColors_true); + return true; + case PartyShare::UNKNOWN: + // TRANSLATORS: chat message + tab->chatLog(_("Item sharing unknown."), + ChatMsgType::BY_SERVER, + IgnoreRecord_false, + TryRemoveColors_true); + return true; + default: + break; + } + } + + const signed char opt = parseBoolean(args); + + switch (opt) + { + case 1: + partyHandler->setShareItems( + PartyShare::YES); + break; + case 0: + partyHandler->setShareItems( + PartyShare::NO); + break; + case -1: + tab->chatLog(strprintf(BOOLEAN_OPTIONS, "item"), + ChatMsgType::BY_SERVER, + IgnoreRecord_false, + TryRemoveColors_true); + break; + default: + break; + } + return true; +} + +impHandler(partyExpShare) +{ + if (localPlayer == nullptr) + return false; + + if (localPlayer->isInParty() == false) + return true; + + ChatTab *tab = event.tab; + if (tab == nullptr) + tab = localChatTab; + if (tab == nullptr) + return true; + + const std::string args = event.args; + if (args.empty()) + { + switch (partyHandler->getShareExperience()) + { + case PartyShare::YES: + // TRANSLATORS: chat message + tab->chatLog(_("Experience sharing enabled."), + ChatMsgType::BY_SERVER, + IgnoreRecord_false, + TryRemoveColors_true); + return true; + case PartyShare::NO: + // TRANSLATORS: chat message + tab->chatLog(_("Experience sharing disabled."), + ChatMsgType::BY_SERVER, + IgnoreRecord_false, + TryRemoveColors_true); + return true; + case PartyShare::NOT_POSSIBLE: + // TRANSLATORS: chat message + tab->chatLog(_("Experience sharing not possible."), + ChatMsgType::BY_SERVER, + IgnoreRecord_false, + TryRemoveColors_true); + return true; + case PartyShare::UNKNOWN: + // TRANSLATORS: chat message + tab->chatLog(_("Experience sharing unknown."), + ChatMsgType::BY_SERVER, + IgnoreRecord_false, + TryRemoveColors_true); + return true; + default: + break; + } + } + + const signed char opt = parseBoolean(args); + + switch (opt) + { + case 1: + partyHandler->setShareExperience( + PartyShare::YES); + break; + case 0: + partyHandler->setShareExperience( + PartyShare::NO); + break; + case -1: + tab->chatLog(strprintf(BOOLEAN_OPTIONS, "exp"), + ChatMsgType::BY_SERVER, + IgnoreRecord_false, + TryRemoveColors_true); + break; + default: + break; + } + return true; +} + +impHandler(partyAutoItemShare) +{ + if (localPlayer == nullptr) + return false; + + if (localPlayer->isInParty() == false) + return true; + + ChatTab *tab = event.tab; + if (tab == nullptr) + tab = localChatTab; + if (tab == nullptr) + return true; + + const std::string args = event.args; + if (args.empty()) + { + switch (partyHandler->getShareAutoItems()) + { + case PartyShare::YES: + // TRANSLATORS: chat message + tab->chatLog(_("Auto item sharing enabled."), + ChatMsgType::BY_SERVER, + IgnoreRecord_false, + TryRemoveColors_true); + return true; + case PartyShare::NO: + // TRANSLATORS: chat message + tab->chatLog(_("Auto item sharing disabled."), + ChatMsgType::BY_SERVER, + IgnoreRecord_false, + TryRemoveColors_true); + return true; + case PartyShare::NOT_POSSIBLE: + // TRANSLATORS: chat message + tab->chatLog(_("Auto item sharing not possible."), + ChatMsgType::BY_SERVER, + IgnoreRecord_false, + TryRemoveColors_true); + return true; + case PartyShare::UNKNOWN: + // TRANSLATORS: chat message + tab->chatLog(_("Auto item sharing unknown."), + ChatMsgType::BY_SERVER, + IgnoreRecord_false, + TryRemoveColors_true); + return true; + default: + break; + } + } + + const signed char opt = parseBoolean(args); + + switch (opt) + { + case 1: + partyHandler->setShareAutoItems( + PartyShare::YES); + break; + case 0: + partyHandler->setShareAutoItems( + PartyShare::NO); + break; + case -1: + tab->chatLog(strprintf(BOOLEAN_OPTIONS, "item"), + ChatMsgType::BY_SERVER, + IgnoreRecord_false, + TryRemoveColors_true); + break; + default: + break; + } + return true; +} + +impHandler0(outfitToChat) +{ + if ((outfitWindow == nullptr) || (chatWindow == nullptr)) + return false; + + const std::string str = outfitWindow->getOutfitString(); + if (!str.empty()) + chatWindow->addInputText(str, true); + return true; +} + +impHandler0(outfitClear) +{ + if (outfitWindow == nullptr) + return false; + + outfitWindow->clearCurrentOutfit(); + return true; +} + +impHandler(moveAttackUp) +{ + if (actorManager == nullptr) + return false; + const std::string args = event.args; + const int idx = actorManager->getAttackMobIndex(args); + if (idx > 0) + { + std::list<std::string> mobs + = actorManager->getAttackMobs(); + std::list<std::string>::iterator it = mobs.begin(); + std::list<std::string>::iterator it2 = it; + while (it != mobs.end()) + { + if (*it == args) + { + -- it2; + mobs.splice(it2, mobs, it); + actorManager->setAttackMobs(mobs); + actorManager->rebuildAttackMobs(); + break; + } + ++ it; + ++ it2; + } + + if (socialWindow != nullptr) + socialWindow->updateAttackFilter(); + return true; + } + return false; +} + +impHandler(moveAttackDown) +{ + if (actorManager == nullptr) + return false; + const std::string args = event.args; + const int idx = actorManager->getAttackMobIndex(args); + const int size = actorManager->getAttackMobsSize(); + if (idx + 1 < size) + { + std::list<std::string> mobs + = actorManager->getAttackMobs(); + std::list<std::string>::iterator it = mobs.begin(); + std::list<std::string>::iterator it2 = it; + while (it != mobs.end()) + { + if (*it == args) + { + ++ it2; + if (it2 == mobs.end()) + break; + + mobs.splice(it, mobs, it2); + actorManager->setAttackMobs(mobs); + actorManager->rebuildAttackMobs(); + break; + } + ++ it; + ++ it2; + } + + if (socialWindow != nullptr) + socialWindow->updateAttackFilter(); + return true; + } + return false; +} + +impHandler(movePriorityAttackUp) +{ + if (actorManager == nullptr) + return false; + const std::string args = event.args; + const int idx = actorManager-> + getPriorityAttackMobIndex(args); + if (idx > 0) + { + std::list<std::string> mobs + = actorManager->getPriorityAttackMobs(); + std::list<std::string>::iterator it = mobs.begin(); + std::list<std::string>::iterator it2 = it; + while (it != mobs.end()) + { + if (*it == args) + { + -- it2; + mobs.splice(it2, mobs, it); + actorManager->setPriorityAttackMobs(mobs); + actorManager->rebuildPriorityAttackMobs(); + break; + } + ++ it; + ++ it2; + } + + if (socialWindow != nullptr) + socialWindow->updateAttackFilter(); + return true; + } + return false; +} + +impHandler(movePriorityAttackDown) +{ + if (actorManager == nullptr) + return false; + const std::string args = event.args; + const int idx = actorManager + ->getPriorityAttackMobIndex(args); + const int size = actorManager->getPriorityAttackMobsSize(); + if (idx + 1 < size) + { + std::list<std::string> mobs + = actorManager->getPriorityAttackMobs(); + std::list<std::string>::iterator it = mobs.begin(); + std::list<std::string>::iterator it2 = it; + while (it != mobs.end()) + { + if (*it == args) + { + ++ it2; + if (it2 == mobs.end()) + break; + + mobs.splice(it, mobs, it2); + actorManager->setPriorityAttackMobs(mobs); + actorManager->rebuildPriorityAttackMobs(); + break; + } + ++ it; + ++ it2; + } + + if (socialWindow != nullptr) + socialWindow->updateAttackFilter(); + return true; + } + return false; +} + +impHandler(addSkillShortcut) +{ + const std::string args = event.args; + if (args.empty() || + itemShortcutWindow == nullptr) + { + return false; + } + const SkillInfo *restrict const skill = skillDialog->getSkill( + atoi(args.c_str())); + if (skill == nullptr) + return false; + + const int num = itemShortcutWindow->getTabIndex(); + if (num < 0 || + num >= CAST_S32(SHORTCUT_TABS) || + num == CAST_S32(SHORTCUT_AUTO_TAB)) + { + return false; + } + + ItemShortcut *const selShortcut = itemShortcut[num]; + const size_t index = selShortcut->getFreeIndex(); + if (index == SHORTCUT_ITEMS) + return true; + + selShortcut->setItem(index, + skill->id + SKILL_MIN_ID, + fromInt(skill->customSelectedLevel, ItemColor)); + selShortcut->setItemData(index, + skill->toDataStr()); + +// popupMenu->showSkillLevelPopup(skill); + return true; +} + +impHandler0(trimMemory) +{ +#ifdef HAVE_MALLOC_TRIM + malloc_trim(0); +#else + // TRANSLATORS: chat error about trim command + localChatTab->chatLog(_("Trim memory not supported"), + ChatMsgType::BY_SERVER, + IgnoreRecord_false, + TryRemoveColors_true); +#endif + return true; +} + +} // namespace Actions diff --git a/src/progs/manaverse/actions/move.cpp b/src/progs/manaverse/actions/move.cpp new file mode 100644 index 000000000..38eff9403 --- /dev/null +++ b/src/progs/manaverse/actions/move.cpp @@ -0,0 +1,281 @@ +/* + * The ManaPlus Client + * Copyright (C) 2012-2020 The ManaPlus Developers + * Copyright (C) 2020-2023 The ManaVerse Developers + * + * This file is part of The ManaPlus Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * 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 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 <http://www.gnu.org/licenses/>. + */ + +#include "actions/move.h" + +#include "game.h" + +#include "actions/actiondef.h" +#include "actions/pets.h" + +#include "being/crazymoves.h" +#include "being/localplayer.h" + +#include "enums/being/beingdirection.h" + +#include "gui/windows/socialwindow.h" +#include "gui/windows/npcdialog.h" +#include "gui/windows/outfitwindow.h" + +#include "gui/popups/popupmenu.h" + +#include "input/inputactionoperators.h" + +#include "net/playerhandler.h" + +#include "debug.h" + +namespace Actions +{ + +static bool closeMoveNpcDialog(bool focus) +{ + NpcDialog *const dialog = NpcDialog::getActive(); + if (dialog != nullptr) + { + if (dialog->isCloseState() != 0) + { + dialog->closeDialog(); + return true; + } + else if (focus) + { + dialog->refocus(); + } + } + return false; +} + +impHandler(moveUp) +{ + if (inputManager.isActionActive(InputAction::EMOTE)) + return directUp(event); + else if (inputManager.isActionActive(InputAction::PET_EMOTE)) + return petDirectUp(event); + else if (inputManager.isActionActive(InputAction::STOP_ATTACK)) + return petMoveUp(event); + else if (!localPlayer->canMove()) + return directUp(event); + if (popupMenu->isPopupVisible()) + { + popupMenu->moveUp(); + return true; + } + return closeMoveNpcDialog(false); +} + +impHandler(moveDown) +{ + if (inputManager.isActionActive(InputAction::EMOTE)) + return directDown(event); + else if (inputManager.isActionActive(InputAction::PET_EMOTE)) + return petDirectDown(event); + else if (inputManager.isActionActive(InputAction::STOP_ATTACK)) + return petMoveDown(event); + else if (!localPlayer->canMove()) + return directDown(event); + if (popupMenu->isPopupVisible()) + { + popupMenu->moveDown(); + return true; + } + return closeMoveNpcDialog(false); +} + +impHandler(moveLeft) +{ + if (outfitWindow != nullptr && + inputManager.isActionActive(InputAction::WEAR_OUTFIT)) + { + outfitWindow->wearPreviousOutfit(false); + if (Game::instance() != nullptr) + Game::instance()->setValidSpeed(); + return true; + } + if (inputManager.isActionActive(InputAction::EMOTE)) + return directLeft(event); + else if (inputManager.isActionActive(InputAction::PET_EMOTE)) + return petDirectLeft(event); + else if (inputManager.isActionActive(InputAction::STOP_ATTACK)) + return petMoveLeft(event); + else if (!localPlayer->canMove()) + return directLeft(event); + return closeMoveNpcDialog(false); +} + +impHandler(moveRight) +{ + if (outfitWindow != nullptr && + inputManager.isActionActive(InputAction::WEAR_OUTFIT)) + { + outfitWindow->wearNextOutfit(false); + if (Game::instance() != nullptr) + Game::instance()->setValidSpeed(); + return true; + } + if (inputManager.isActionActive(InputAction::EMOTE)) + return directRight(event); + else if (inputManager.isActionActive(InputAction::PET_EMOTE)) + return petDirectRight(event); + else if (inputManager.isActionActive(InputAction::STOP_ATTACK)) + return petMoveRight(event); + else if (!localPlayer->canMove()) + return directRight(event); + return closeMoveNpcDialog(false); +} + +impHandler(moveForward) +{ + if (inputManager.isActionActive(InputAction::EMOTE)) + return directRight(event); + return closeMoveNpcDialog(false); +} + +impHandler(moveToPoint) +{ + const int num = event.action - InputAction::MOVE_TO_POINT_1; + if ((socialWindow != nullptr) && num >= 0) + { + socialWindow->selectPortal(num); + return true; + } + + return false; +} + +impHandler0(crazyMoves) +{ + if (localPlayer != nullptr) + { + ::crazyMoves->crazyMove(); + return true; + } + return false; +} + +impHandler0(moveToTarget) +{ + if (localPlayer != nullptr && + !inputManager.isActionActive(InputAction::TARGET_ATTACK) && + !inputManager.isActionActive(InputAction::ATTACK)) + { + localPlayer->moveToTarget(-1); + return true; + } + return false; +} + +impHandler0(moveToHome) +{ + if (localPlayer != nullptr && + !inputManager.isActionActive(InputAction::TARGET_ATTACK) && + !inputManager.isActionActive(InputAction::ATTACK)) + { + localPlayer->moveToHome(); + if (Game::instance() != nullptr) + Game::instance()->setValidSpeed(); + return true; + } + return false; +} + +impHandler0(directUp) +{ + if (localPlayer != nullptr) + { + if (localPlayer->getDirection() != BeingDirection::UP) + { +// if (PacketLimiter::limitPackets(PacketType::PACKET_DIRECTION)) + { + localPlayer->setDirection(BeingDirection::UP); + if (playerHandler != nullptr) + playerHandler->setDirection(BeingDirection::UP); + } + } + return true; + } + return false; +} + +impHandler0(directDown) +{ + if (localPlayer != nullptr) + { + if (localPlayer->getDirection() != BeingDirection::DOWN) + { +// if (PacketLimiter::limitPackets(PacketType::PACKET_DIRECTION)) + { + localPlayer->setDirection(BeingDirection::DOWN); + if (playerHandler != nullptr) + { + playerHandler->setDirection( + BeingDirection::DOWN); + } + } + } + return true; + } + return false; +} + +impHandler0(directLeft) +{ + if (localPlayer != nullptr) + { + if (localPlayer->getDirection() != BeingDirection::LEFT) + { +// if (PacketLimiter::limitPackets(PacketType::PACKET_DIRECTION)) + { + localPlayer->setDirection(BeingDirection::LEFT); + if (playerHandler != nullptr) + { + playerHandler->setDirection( + BeingDirection::LEFT); + } + } + } + return true; + } + return false; +} + +impHandler0(directRight) +{ + if (localPlayer != nullptr) + { + if (localPlayer->getDirection() != BeingDirection::RIGHT) + { +// if (PacketLimiter::limitPackets(PacketType::PACKET_DIRECTION)) + { + localPlayer->setDirection(BeingDirection::RIGHT); + if (playerHandler != nullptr) + { + playerHandler->setDirection( + BeingDirection::RIGHT); + } + } + } + return true; + } + return false; +} + +} // namespace Actions diff --git a/src/progs/manaverse/actions/pets.cpp b/src/progs/manaverse/actions/pets.cpp new file mode 100644 index 000000000..f4c3823a3 --- /dev/null +++ b/src/progs/manaverse/actions/pets.cpp @@ -0,0 +1,250 @@ +/* + * The ManaPlus Client + * Copyright (C) 2012-2020 The ManaPlus Developers + * Copyright (C) 2020-2023 The ManaVerse Developers + * + * This file is part of The ManaPlus Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * 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 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 <http://www.gnu.org/licenses/>. + */ + +#include "actions/pets.h" + +#include "actormanager.h" +#include "game.h" + +#include "actions/actiondef.h" + +#include "being/localplayer.h" +#include "being/playerinfo.h" + +#include "enums/being/beingdirection.h" + +#include "input/inputactionoperators.h" + +#include "listeners/inputactionreplaylistener.h" + +#include "gui/shortcut/emoteshortcut.h" + +#include "net/chathandler.h" +#include "net/pethandler.h" +#include "net/serverfeatures.h" + +#include "utils/chatutils.h" +#include "utils/gettext.h" +#include "utils/stringutils.h" + +#include "debug.h" + +namespace Actions +{ + +static const Being *getPet() +{ + const BeingId id = PlayerInfo::getPetBeingId(); + if (id == BeingId_zero) + return nullptr; + return actorManager->findBeing(id); +} + +impHandler(commandEmotePet) +{ + petHandler->emote(CAST_U8(atoi(event.args.c_str()))); + return true; +} + +impHandler(talkPet) +{ + if (!serverFeatures->haveTalkPet()) + return false; + + std::string args = event.args; + if (findCutFirst(args, "/me ")) + args = textToMe(args); + chatHandler->talkPet(args); + return true; +} + +impHandler(setPetName) +{ + const std::string args = event.args; + if (args.empty()) + { + const Being *const pet = getPet(); + if (pet == nullptr) + return false; + // TRANSLATORS: dialog header + inputActionReplayListener.openDialog(_("Rename your pet"), + pet->getName(), + InputAction::PET_SET_NAME); + return false; + } + + petHandler->setName(args); + return true; +} + +impHandler(petEmote) +{ + if (!serverFeatures->haveTalkPet()) + return false; + + if (event.action >= InputAction::PET_EMOTE_1 + && event.action <= InputAction::PET_EMOTE_48) + { + if (emoteShortcut != nullptr) + { + const int emotion = event.action - InputAction::PET_EMOTE_1; + petHandler->emote(emoteShortcut->getEmote(emotion)); + } + if (Game::instance() != nullptr) + Game::instance()->setValidSpeed(); + return true; + } + + return false; +} + +impHandler(catchPet) +{ + if ((localPlayer == nullptr) || (actorManager == nullptr)) + return false; + + Being *target = nullptr; + const std::string args = event.args; + if (!args.empty()) + { + if (args[0] == ':') + { + target = actorManager->findBeing(fromInt(atoi( + args.substr(1).c_str()), BeingId)); + } + else + { + target = actorManager->findNearestByName(args, + ActorType::Unknown); + } + } + + if (target == nullptr) + target = localPlayer->getTarget(); + else + localPlayer->setTarget(target); + if (target != nullptr) + petHandler->catchPet(target); + return true; +} + +impHandler0(petMoveUp) +{ + const Being *const pet = getPet(); + if (pet == nullptr) + return false; + petHandler->move(pet->getTileX(), pet->getTileY() - 1); + return true; +} + +impHandler0(petMoveDown) +{ + const Being *const pet = getPet(); + if (pet == nullptr) + return false; + petHandler->move(pet->getTileX(), pet->getTileY() + 1); + return true; +} + +impHandler0(petMoveLeft) +{ + const Being *const pet = getPet(); + if (pet == nullptr) + return false; + petHandler->move(pet->getTileX() - 1, pet->getTileY()); + return true; +} + +impHandler0(petMoveRight) +{ + const Being *const pet = getPet(); + if (pet == nullptr) + return false; + petHandler->move(pet->getTileX() + 1, pet->getTileY()); + return true; +} + +impHandler0(petDirectUp) +{ + petHandler->setDirection(BeingDirection::UP); + return true; +} + +impHandler0(petDirectDown) +{ + petHandler->setDirection(BeingDirection::DOWN); + return true; +} + +impHandler0(petDirectLeft) +{ + petHandler->setDirection(BeingDirection::LEFT); + return true; +} + +impHandler0(petDirectRight) +{ + petHandler->setDirection(BeingDirection::RIGHT); + return true; +} + +impHandler(petMove) +{ + int x = 0; + int y = 0; + + if (parse2Int(event.args, x, y)) + { + petHandler->move(x, y); + return true; + } + return false; +} + +impHandler0(petFeed) +{ + if (petHandler != nullptr) + petHandler->feed(); + return true; +} + +impHandler0(petDropLoot) +{ + if (petHandler != nullptr) + petHandler->dropLoot(); + return true; +} + +impHandler0(petReturnToEgg) +{ + if (petHandler != nullptr) + petHandler->returnToEgg(); + return true; +} + +impHandler0(petUnequip) +{ + if (petHandler != nullptr) + petHandler->unequip(); + return true; +} + +} // namespace Actions diff --git a/src/progs/manaverse/actions/statusbar.cpp b/src/progs/manaverse/actions/statusbar.cpp new file mode 100644 index 000000000..062d42a53 --- /dev/null +++ b/src/progs/manaverse/actions/statusbar.cpp @@ -0,0 +1,205 @@ +/* + * The ManaPlus Client + * Copyright (C) 2012-2020 The ManaPlus Developers + * Copyright (C) 2020-2023 The ManaVerse Developers + * + * This file is part of The ManaPlus Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * 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 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 <http://www.gnu.org/licenses/>. + */ + +#include "actions/statusbar.h" + +#include "game.h" +#include "soundmanager.h" + +#include "actions/actiondef.h" + +#include "being/localplayer.h" +#include "being/playerrelation.h" +#include "being/playerrelations.h" + +#include "gui/viewport.h" + +#include "gui/widgets/tabs/chat/chattab.h" + +#include "listeners/updatestatuslistener.h" + +#include "resources/map/map.h" + +#include "utils/gettext.h" + +PRAGMA48(GCC diagnostic push) +PRAGMA48(GCC diagnostic ignored "-Wshadow") +#ifdef ANDROID +#ifndef USE_SDL2 +#include <SDL_screenkeyboard.h> +#endif // USE_SDL2 +#endif // ANDROID +PRAGMA48(GCC diagnostic pop) + +#include "debug.h" + +namespace Actions +{ + +impHandler0(switchQuickDrop) +{ + callYellowBarCond(changeQuickDropCounter); +} + +impHandler0(changeCrazyMove) +{ + callYellowBar(changeCrazyMoveType); +} + +impHandler0(changePickupType) +{ + callYellowBar(changePickUpType); +} + +impHandler0(changeMoveType) +{ + callYellowBar(changeMoveType); +} + +impHandler0(changeAttackWeaponType) +{ + callYellowBar(changeAttackWeaponType); +} + +impHandler0(changeAttackType) +{ + callYellowBar(changeAttackType); +} + +impHandler0(changeTargetingType) +{ + callYellowBar(changeTargetingType); +} + +impHandler0(changeFollowMode) +{ + callYellowBar(changeFollowMode); +} + +impHandler0(changeImitationMode) +{ + callYellowBar(changeImitationMode); +} + +impHandler0(changeMagicAttackType) +{ + callYellowBar(changeMagicAttackType); +} + +impHandler0(changePvpMode) +{ + callYellowBar(changePvpAttackType); +} + +impHandler0(changeMoveToTarget) +{ + callYellowBar(changeMoveToTargetType); +} + +impHandler0(changeGameModifier) +{ + if (localPlayer != nullptr) + { + GameModifiers::changeGameModifiers(false); + return true; + } + return false; +} + +impHandler0(changeAudio) +{ + soundManager.changeAudio(); + if (localPlayer != nullptr) + localPlayer->updateMusic(); + return true; +} + +impHandler0(away) +{ + GameModifiers::changeAwayMode(true); + if (localPlayer != nullptr) + { + localPlayer->updateStatus(); + if (Game::instance() != nullptr) + Game::instance()->setValidSpeed(); + return true; + } + return false; +} + +impHandler0(camera) +{ + if (viewport != nullptr) + { + viewport->toggleCameraMode(); + if (Game::instance() != nullptr) + Game::instance()->setValidSpeed(); + return true; + } + return false; +} + +impHandler0(changeMapMode) +{ + if (viewport != nullptr) + viewport->toggleMapDrawType(); + UpdateStatusListener::distributeEvent(); + if (Game::instance() != nullptr) + { + if (Map *const map = Game::instance()->getCurrentMap()) + map->redrawMap(); + } + return true; +} + +impHandler0(changeTrade) +{ + unsigned int deflt = playerRelations.getDefault(); + if ((deflt & PlayerRelation::TRADE) != 0U) + { + if (localChatTab != nullptr) + { + // TRANSLATORS: disable trades message + localChatTab->chatLog(_("Ignoring incoming trade requests"), + ChatMsgType::BY_SERVER, + IgnoreRecord_false, + TryRemoveColors_true); + } + deflt &= ~PlayerRelation::TRADE; + } + else + { + if (localChatTab != nullptr) + { + // TRANSLATORS: enable trades message + localChatTab->chatLog(_("Accepting incoming trade requests"), + ChatMsgType::BY_SERVER, + IgnoreRecord_false, + TryRemoveColors_true); + } + deflt |= PlayerRelation::TRADE; + } + + playerRelations.setDefault(deflt); + return true; +} + +} // namespace Actions diff --git a/src/progs/manaverse/actions/tabs.cpp b/src/progs/manaverse/actions/tabs.cpp new file mode 100644 index 000000000..b5140a2e9 --- /dev/null +++ b/src/progs/manaverse/actions/tabs.cpp @@ -0,0 +1,107 @@ +/* + * The ManaPlus Client + * Copyright (C) 2012-2020 The ManaPlus Developers + * Copyright (C) 2020-2023 The ManaVerse Developers + * + * This file is part of The ManaPlus Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * 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 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 <http://www.gnu.org/licenses/>. + */ + +#include "actions/tabs.h" + +#include "actions/actiondef.h" + +#include "gui/windows/inventorywindow.h" +#include "gui/windows/socialwindow.h" +#include "gui/windows/shortcutwindow.h" + +#include "debug.h" + +namespace Actions +{ + +impHandler0(prevSocialTab) +{ + if (socialWindow != nullptr) + { + socialWindow->prevTab(); + return true; + } + return false; +} + +impHandler0(nextSocialTab) +{ + if (socialWindow != nullptr) + { + socialWindow->nextTab(); + return true; + } + return false; +} + +impHandler0(nextShortcutsTab) +{ + if (itemShortcutWindow != nullptr) + { + itemShortcutWindow->nextTab(); + return true; + } + return false; +} + +impHandler0(prevShortcutsTab) +{ + if (itemShortcutWindow != nullptr) + { + itemShortcutWindow->prevTab(); + return true; + } + return false; +} + +impHandler0(nextCommandsTab) +{ + if (spellShortcutWindow != nullptr) + { + spellShortcutWindow->nextTab(); + return true; + } + return false; +} + +impHandler0(prevCommandsTab) +{ + if (spellShortcutWindow != nullptr) + { + spellShortcutWindow->prevTab(); + return true; + } + return false; +} + +impHandler0(nextInvTab) +{ + InventoryWindow::nextTab(); + return true; +} + +impHandler0(prevInvTab) +{ + InventoryWindow::prevTab(); + return true; +} + +} // namespace Actions diff --git a/src/progs/manaverse/actions/target.cpp b/src/progs/manaverse/actions/target.cpp new file mode 100644 index 000000000..9b97cf576 --- /dev/null +++ b/src/progs/manaverse/actions/target.cpp @@ -0,0 +1,91 @@ +/* + * The ManaPlus Client + * Copyright (C) 2012-2020 The ManaPlus Developers + * Copyright (C) 2020-2023 The ManaVerse Developers + * + * This file is part of The ManaPlus Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * 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 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 <http://www.gnu.org/licenses/>. + */ + +#include "actions/target.h" + +#include "actions/actiondef.h" + +#include "being/localplayer.h" + +#include "gui/popups/popupmenu.h" + +#include "debug.h" + +namespace Actions +{ + +static bool setTarget(const ActorTypeT type, const AllowSort allowSort) +{ + if (localPlayer != nullptr) + return localPlayer->setNewTarget(type, allowSort) != nullptr; + return false; +} + +impHandler0(targetPlayer) +{ + return setTarget(ActorType::Player, AllowSort_true); +} + +impHandler0(targetMonster) +{ + return setTarget(ActorType::Monster, AllowSort_true); +} + +impHandler0(targetClosestMonster) +{ + return setTarget(ActorType::Monster, AllowSort_false); +} + +impHandler0(targetNPC) +{ + return setTarget(ActorType::Npc, AllowSort_true); +} + +impHandler0(targetMercenary) +{ + return setTarget(ActorType::Mercenary, AllowSort_true); +} + +impHandler0(targetSkillUnit) +{ + return setTarget(ActorType::SkillUnit, AllowSort_true); +} + +impHandler0(targetPet) +{ + return setTarget(ActorType::Pet, AllowSort_true); +} + +impHandler0(contextMenu) +{ + if (localPlayer == nullptr) + return false; + const Being *const target = localPlayer->getTarget(); + if (target == nullptr) + return true; + + popupMenu->showPopup(target->getPixelX(), + target->getPixelY(), + target); + return true; +} + +} // namespace Actions diff --git a/src/progs/manaverse/actions/windows.cpp b/src/progs/manaverse/actions/windows.cpp new file mode 100644 index 000000000..9f8a68faa --- /dev/null +++ b/src/progs/manaverse/actions/windows.cpp @@ -0,0 +1,393 @@ +/* + * The ManaPlus Client + * Copyright (C) 2012-2020 The ManaPlus Developers + * Copyright (C) 2020-2023 The ManaVerse Developers + * + * This file is part of The ManaPlus Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * 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 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 <http://www.gnu.org/licenses/>. + */ + +#include "actions/windows.h" + +#include "actormanager.h" +#include "client.h" + +#include "actions/actiondef.h" + +#include "being/localplayer.h" + +#include "gui/dialogsmanager.h" + +#include "gui/windows/bankwindow.h" +#include "gui/windows/clanwindow.h" +#include "gui/windows/skilldialog.h" +#include "gui/windows/socialwindow.h" +#include "gui/windows/statuswindow.h" +#include "gui/windows/questswindow.h" +#include "gui/windows/whoisonline.h" +#include "gui/windows/chatwindow.h" +#include "gui/windows/debugwindow.h" +#include "gui/windows/didyouknowwindow.h" +#include "gui/windows/equipmentwindow.h" +#include "gui/windows/helpwindow.h" +#include "gui/windows/inventorywindow.h" +#include "gui/windows/killstats.h" +#include "gui/windows/mailwindow.h" +#include "gui/windows/minimap.h" +#include "gui/windows/outfitwindow.h" +#include "gui/windows/setupwindow.h" +#include "gui/windows/serverinfowindow.h" +#include "gui/windows/shopwindow.h" +#include "gui/windows/shortcutwindow.h" +#include "gui/windows/updaterwindow.h" + +#include "gui/widgets/createwidget.h" + +#include "gui/widgets/tabs/chat/chattab.h" + +#include "utils/gettext.h" + +#include "net/net.h" + +#include "debug.h" + +namespace Actions +{ + +impHandler0(setupWindowShow) +{ + if (setupWindow != nullptr) + { + if (setupWindow->isWindowVisible()) + { + setupWindow->doCancel(); + } + else + { + setupWindow->setVisible(Visible_true); + setupWindow->requestMoveToTop(); + } + return true; + } + return false; +} + +impHandler0(hideWindows) +{ + if (setupWindow != nullptr) + setupWindow->hideWindows(); + return true; +} + +static bool showHelpPage(const std::string &page, const bool showHide) +{ + if (helpWindow != nullptr) + { + if (showHide && helpWindow->isWindowVisible()) + { + helpWindow->setVisible(Visible_false); + } + else + { + helpWindow->loadHelp(page); + helpWindow->requestMoveToTop(); + } + return true; + } + return false; +} + +impHandler(helpWindowShow) +{ + if ((chatWindow == nullptr) || !chatWindow->isInputFocused()) + return showHelpPage("index", true); + if (event.tab == nullptr) + return showHelpPage("chatcommands", true); + switch (event.tab->getType()) + { + case ChatTabType::PARTY: + return showHelpPage("chatparty", true); + case ChatTabType::GUILD: + return showHelpPage("chatguild", true); + case ChatTabType::WHISPER: + return showHelpPage("chatwhisper", true); + case ChatTabType::DEBUG: + return showHelpPage("chatdebug", true); + case ChatTabType::TRADE: + return showHelpPage("chattrade", true); + case ChatTabType::BATTLE: + return showHelpPage("chatbattle", true); + case ChatTabType::LANG: + return showHelpPage("chatlang", true); + case ChatTabType::GM: + return showHelpPage("chatgm", true); + case ChatTabType::CHANNEL: + return showHelpPage("chatchannel", true); + case ChatTabType::CLAN: + return showHelpPage("chatclan", true); + default: + case ChatTabType::UNKNOWN: + case ChatTabType::INPUT: + return showHelpPage("chatcommands", true); + } +} + +impHandler0(aboutWindowShow) +{ + return showHelpPage("about", false); +} + +static void showHideWindow(Window *const window) +{ + if (window != nullptr) + { + window->setVisible(fromBool( + !window->isWindowVisible(), Visible)); + if (window->isWindowVisible()) + window->requestMoveToTop(); + } +} + +impHandler0(statusWindowShow) +{ + showHideWindow(statusWindow); + return true; +} + +impHandler0(inventoryWindowShow) +{ + showHideWindow(inventoryWindow); + return true; +} + +impHandler0(equipmentWindowShow) +{ + showHideWindow(equipmentWindow); + return true; +} + +impHandler0(skillDialogShow) +{ + showHideWindow(skillDialog); + return true; +} + +impHandler0(minimapWindowShow) +{ + if (minimap != nullptr) + { + minimap->toggle(); + return true; + } + return false; +} + +impHandler0(chatWindowShow) +{ + showHideWindow(chatWindow); + return true; +} + +impHandler0(shortcutWindowShow) +{ + showHideWindow(itemShortcutWindow); + return true; +} + +impHandler0(debugWindowShow) +{ + showHideWindow(debugWindow); + return true; +} + +impHandler0(socialWindowShow) +{ + showHideWindow(socialWindow); + return true; +} + +impHandler0(emoteShortcutWindowShow) +{ + showHideWindow(emoteShortcutWindow); + return true; +} + +impHandler0(outfitWindowShow) +{ + showHideWindow(outfitWindow); + return true; +} + +impHandler0(shopWindowShow) +{ + showHideWindow(shopWindow); + return true; +} + +impHandler0(dropShortcutWindowShow) +{ + showHideWindow(dropShortcutWindow); + return true; +} + +impHandler0(killStatsWindowShow) +{ + showHideWindow(killStats); + return true; +} + +impHandler0(spellShortcutWindowShow) +{ + showHideWindow(spellShortcutWindow); + return true; +} + +impHandler0(whoIsOnlineWindowShow) +{ + showHideWindow(whoIsOnline); + return true; +} + +impHandler0(didYouKnowWindowShow) +{ + showHideWindow(didYouKnowWindow); + return true; +} + +impHandler0(questsWindowShow) +{ + showHideWindow(questsWindow); + return true; +} + +impHandler0(bankWindowShow) +{ +#ifdef TMWA_SUPPORT + if (Net::getNetworkType() == ServerType::TMWATHENA) + return false; +#endif // TMWA_SUPPORT + + showHideWindow(bankWindow); + return true; +} + +impHandler0(cartWindowShow) +{ + if (Net::getNetworkType() == ServerType::TMWATHENA || + (localPlayer == nullptr) || + !localPlayer->getHaveCart()) + { + return false; + } + + showHideWindow(cartWindow); + if (inventoryWindow != nullptr) + inventoryWindow->updateDropButton(); + return true; +} + +impHandler0(updaterWindowShow) +{ + if (updaterWindow != nullptr) + updaterWindow->deleteSelf(); + else + DialogsManager::createUpdaterWindow(); + return true; +} + +impHandler0(quickWindowShow) +{ + if (setupWindow != nullptr) + { + if (setupWindow->isWindowVisible()) + setupWindow->doCancel(); + setupWindow->setVisible(Visible_true); + // TRANSLATORS: settings tab name + setupWindow->activateTab(_("Quick")); + setupWindow->requestMoveToTop(); + return true; + } + return false; +} + +impHandler0(mailWindowShow) +{ + showHideWindow(mailWindow); + return true; +} + +impHandler0(serverInfoWindowShow) +{ + if (serverInfoWindow != nullptr && + serverInfoWindow->isWindowVisible()) + { + serverInfoWindow->close(); + serverInfoWindow = nullptr; + } + else + { + serverInfoWindow = CREATEWIDGETR(ServerInfoWindow, + client->getCurrentServer()); + serverInfoWindow->requestMoveToTop(); + } + return true; +} + +impHandler0(clanWindowShow) +{ + showHideWindow(clanWindow); + return true; +} + +impHandler(showItems) +{ + const std::string args = event.args; + if (args.empty()) + return false; + + Being *being = nullptr; + if (args[0] == ':') + { + being = actorManager->findBeing(fromInt(atoi( + args.substr(1).c_str()), BeingId)); + if ((being != nullptr) && being->getType() == ActorType::Monster) + being = nullptr; + } + else + { + being = actorManager->findBeingByName(args, ActorType::Player); + } + if (being == nullptr) + return true; + if (being == localPlayer) + { + if (equipmentWindow != nullptr && + !equipmentWindow->isWindowVisible()) + { + equipmentWindow->setVisible(Visible_true); + } + } + else + { + if (beingEquipmentWindow != nullptr) + { + beingEquipmentWindow->setBeing(being); + beingEquipmentWindow->setVisible(Visible_true); + } + } + return true; +} + +} // namespace Actions |