diff options
Diffstat (limited to 'src/progs/manaverse/actions/actions.cpp')
-rw-r--r-- | src/progs/manaverse/actions/actions.cpp | 2048 |
1 files changed, 2048 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 |